summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2020-06-04 11:53:13 -0700
committerGitHub <noreply@github.com>2020-06-04 11:53:13 -0700
commitf3d637ba4d90bc2e23db07f1a9df5a6be7533f08 (patch)
tree3dfe2fd73309ed4caf4ad6d4e8ee7a138296dfc3 /source/slang
parent1b8731c809761c4e2dbec81dcee207f8a4621903 (diff)
First steps toward inheritance for struct types (#1366)
* First steps toward inheritance for struct types This change adds the ability for a `struct` type to declare a base type that is another `struct`: ```hlsl struct Base { int baseMember; } struct Derived : Base { int derivedMember; } ``` The semantics of the feature are that code like the above desugars into code like: ```hlsl struct Base { int baseMember; } struct Derived { Base _base; int derivedMember; } ``` At points where a member from the base type is being projected out, or the value is being implicitly cast to the base type, the compiler transforms the code to reference the implicitly-generated `_base` member. That means code like this: ```hlsl void f(Base b); ... Derived d = ...; int x = d.baseMember; f(d); ``` gets transformed into a form like this: ```hlsl void f(Base b); ... Derived d = ...; int x = d._base.baseMember; f(d._base); ``` Note that as a result of this choice, the behavior when passing a `Derived` value to a function that expects a `Base` (including to inherited member functions) is that of "object shearing" from the C++ world: the called function can only "see" the `Base` part of the argument, and any operations performed on it will behave as if the value was indeed a `Base`. There is no polymorphism going on because Slang doesn't currently have `virtual` methods. In an attempt to work toward inheritance being a robust feature, this change adds a bunch of more detailed logic for checking the bases of various declarations: * An `interface` declaration is only allowed to inherit from other `interface`s * An `extension` declaration can only introduce inheritance from `interface`s * A `struct` declaration can only inherit from at most one other `struct`, and that `struct` must be the first entry in the list of bases This change also adds a mechanism to control whether a `struct` or `interface` in one module can inherit from a `struct` or `interface` declared in another module: * If the base declaration is marked `[open]`, then the inheritance is allowed * If the base declaration is marked `[sealed]`, then the inheritance is allowed * If it is not marked otherwise, a `struct` is implicitly `[sealed]` * If it is not marked otherwise, an `interface` is implicitly `[open]` These seem like reasonable defaults. In order to safeguard the standard library a bit, the interfaces for builtin types have been marked `[sealed]` to make sure that a user cannot declare a `struct` and then mark it as a `BuiltinFloatingPointType`. This step should bring us a bit closer to being able to document and expose these interfaces for built-in types so that users can write code that is generic over them. There are some big caveats with this work, such that it really only represents a stepping-stone toward a usable inheritance feature. The most important caveats are: * If a `Derived` type tries to conform to an interface, such that one or more interface requirements are satisfied with members inherited from the `Base` type, that is likely to cause a crash or incorrect code generation. * If a `Derived` type tries to inherit from a `Base` type that conforms to one or more interfaces, the witness table generated for the conformance of `Derived` to that interface is likely to lead to a crash or incorrect code generation. It is clear that solving both of those issues will be necessary before we can really promote `struct` inheritance as a feature for users to try out. * fixup: trying to appease clang error * fixups: review feedback
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/core.meta.slang14
-rw-r--r--source/slang/slang-ast-expr.h16
-rw-r--r--source/slang/slang-ast-modifier.h10
-rw-r--r--source/slang/slang-check-conformance.cpp71
-rw-r--r--source/slang/slang-check-constraint.cpp4
-rw-r--r--source/slang/slang-check-conversion.cpp10
-rw-r--r--source/slang/slang-check-decl.cpp377
-rw-r--r--source/slang/slang-check-expr.cpp9
-rw-r--r--source/slang/slang-check-impl.h60
-rw-r--r--source/slang/slang-diagnostic-defs.h16
-rw-r--r--source/slang/slang-lower-to-ir.cpp168
11 files changed, 614 insertions, 141 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 2b43206a3..0c39f2c2f 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -18,9 +18,11 @@ syntax constexpr : ConstExprModifier;
syntax globallycoherent : GloballyCoherentModifier;
// A type that can be used as an operand for builtins
+[sealed]
interface __BuiltinType {}
// A type that can be used for arithmetic operations
+[sealed]
interface __BuiltinArithmeticType : __BuiltinType
{
/// Initialize from a 32-bit signed integer value.
@@ -28,19 +30,24 @@ interface __BuiltinArithmeticType : __BuiltinType
}
/// A type that can be used for logical/bitwsie operations
+[sealed]
interface __BuiltinLogicalType : __BuiltinType {}
// A type that logically has a sign (positive/negative/zero)
+[sealed]
interface __BuiltinSignedArithmeticType : __BuiltinArithmeticType {}
// A type that can represent integers
+[sealed]
interface __BuiltinIntegerType : __BuiltinArithmeticType
{}
// A type that can represent non-integers
+[sealed]
interface __BuiltinRealType : __BuiltinSignedArithmeticType {}
// A type that uses a floating-point representation
+[sealed]
interface __BuiltinFloatingPointType : __BuiltinRealType
{
/// Initialize from a 32-bit floating-point value.
@@ -1917,3 +1924,10 @@ attribute_syntax [__extern] : ExternAttribute;
__attributeTarget(FunctionDeclBase)
attribute_syntax [__unsafeForceInlineEarly] : UnsafeForceInlineEarlyAttribute;
+
+// Inheritance Control
+__attributeTarget(AggTypeDecl)
+attribute_syntax [sealed] : SealedAttribute;
+
+__attributeTarget(AggTypeDecl)
+attribute_syntax [open] : OpenAttribute;
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index e7111e631..96e90ae02 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -228,15 +228,21 @@ class ImplicitCastExpr : public TypeCastExpr
SLANG_CLASS(ImplicitCastExpr)
};
- /// A cast from a value to an interface ("existential") type.
-class CastToInterfaceExpr: public Expr
+ /// A cast of a value to a super-type of its type.
+ ///
+ /// The type being cast to is stored as this expression's `type`.
+ ///
+class CastToSuperTypeExpr: public Expr
{
- SLANG_CLASS(CastToInterfaceExpr)
+ SLANG_CLASS(CastToSuperTypeExpr)
- /// The value being cast to an interface type
+ /// The value being cast to a super type
+ ///
+ /// The type being case from is `valueArg->type`.
+ ///
RefPtr<Expr> valueArg;
- /// A witness showing that `valueArg` conforms to the chosen interface
+ /// A witness showing that `valueArg`'s type is a sub-type of this expression's `type`
RefPtr<Val> witnessArg;
};
diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h
index 065118c7a..4badbd02a 100644
--- a/source/slang/slang-ast-modifier.h
+++ b/source/slang/slang-ast-modifier.h
@@ -873,4 +873,14 @@ class UnsafeForceInlineEarlyAttribute : public Attribute
SLANG_CLASS(UnsafeForceInlineEarlyAttribute)
};
+ /// An attribute that marks a type declaration as either allowing or
+ /// disallowing the type to be inherited from in other modules.
+class InheritanceControlAttribute : public Attribute { SLANG_CLASS(InheritanceControlAttribute) };
+
+ /// An attribute that marks a type declaration as allowing the type to be inherited from in other modules.
+class OpenAttribute : public InheritanceControlAttribute { SLANG_CLASS(OpenAttribute) };
+
+ /// An attribute that marks a type declaration as disallowing the type to be inherited from in other modules.
+class SealedAttribute : public InheritanceControlAttribute { SLANG_CLASS(SealedAttribute) };
+
} // namespace Slang
diff --git a/source/slang/slang-check-conformance.cpp b/source/slang/slang-check-conformance.cpp
index 4d54a93fb..f57e7cb38 100644
--- a/source/slang/slang-check-conformance.cpp
+++ b/source/slang/slang-check-conformance.cpp
@@ -18,15 +18,15 @@ namespace Slang
}
RefPtr<Val> SemanticsVisitor::createTypeWitness(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef,
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef,
TypeWitnessBreadcrumb* inBreadcrumbs)
{
if(!inBreadcrumbs)
{
// We need to construct a witness to the fact
- // that `type` has been proven to be *equal*
- // to `interfaceDeclRef`.
+ // that `subType` has been proven to be *equal*
+ // to `superTypeDeclRef`.
//
SLANG_UNEXPECTED("reflexive type witness");
UNREACHABLE_RETURN(nullptr);
@@ -145,15 +145,15 @@ namespace Slang
}
}
- bool SemanticsVisitor::doesTypeConformToInterfaceImpl(
- RefPtr<Type> originalType,
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef,
+ bool SemanticsVisitor::_isDeclaredSubtype(
+ RefPtr<Type> originalSubType,
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef,
RefPtr<Val>* outWitness,
TypeWitnessBreadcrumb* inBreadcrumbs)
{
// for now look up a conformance member...
- if(auto declRefType = as<DeclRefType>(type))
+ if(auto declRefType = as<DeclRefType>(subType))
{
auto declRef = declRefType->declRef;
@@ -162,11 +162,11 @@ namespace Slang
// TODO: This is actually a bit more complicated, as
// the interface needs to be "object-safe" for us to
// really make this determination...
- if(declRef == interfaceDeclRef)
+ if(declRef == superTypeDeclRef)
{
if(outWitness)
{
- *outWitness = createTypeWitness(originalType, interfaceDeclRef, inBreadcrumbs);
+ *outWitness = createTypeWitness(originalSubType, superTypeDeclRef, inBreadcrumbs);
}
return true;
}
@@ -198,11 +198,11 @@ namespace Slang
TypeWitnessBreadcrumb breadcrumb;
breadcrumb.prev = inBreadcrumbs;
- breadcrumb.sub = type;
+ breadcrumb.sub = subType;
breadcrumb.sup = inheritedType;
breadcrumb.declRef = inheritanceDeclRef;
- if(doesTypeConformToInterfaceImpl(originalType, inheritedType, interfaceDeclRef, outWitness, &breadcrumb))
+ if(_isDeclaredSubtype(originalSubType, inheritedType, superTypeDeclRef, outWitness, &breadcrumb))
{
return true;
}
@@ -214,10 +214,10 @@ namespace Slang
auto inheritedType = getSup(m_astBuilder, genConstraintDeclRef);
TypeWitnessBreadcrumb breadcrumb;
breadcrumb.prev = inBreadcrumbs;
- breadcrumb.sub = type;
+ breadcrumb.sub = subType;
breadcrumb.sup = inheritedType;
breadcrumb.declRef = genConstraintDeclRef;
- if (doesTypeConformToInterfaceImpl(originalType, inheritedType, interfaceDeclRef, outWitness, &breadcrumb))
+ if (_isDeclaredSubtype(originalSubType, inheritedType, superTypeDeclRef, outWitness, &breadcrumb))
{
return true;
}
@@ -252,14 +252,14 @@ namespace Slang
breadcrumb.sup = sup;
breadcrumb.declRef = constraintDeclRef;
- if(doesTypeConformToInterfaceImpl(originalType, sup, interfaceDeclRef, outWitness, &breadcrumb))
+ if(_isDeclaredSubtype(originalSubType, sup, superTypeDeclRef, outWitness, &breadcrumb))
{
return true;
}
}
}
}
- else if(auto taggedUnionType = as<TaggedUnionType>(type))
+ else if(auto taggedUnionType = as<TaggedUnionType>(subType))
{
// A tagged union type conforms to an interface if all of
// the constituent types in the tagged union conform.
@@ -276,10 +276,10 @@ namespace Slang
{
RefPtr<Val> caseWitness;
- if(!doesTypeConformToInterfaceImpl(
+ if(!_isDeclaredSubtype(
caseType,
caseType,
- interfaceDeclRef,
+ superTypeDeclRef,
outWitness ? &caseWitness : nullptr,
nullptr))
{
@@ -304,8 +304,11 @@ namespace Slang
// We will start out being conservative about what we accept
// here, just to keep things simple.
//
- if(!isInterfaceSafeForTaggedUnion(interfaceDeclRef))
- return false;
+ if( auto superInterfaceDeclRef = superTypeDeclRef.as<InterfaceDecl>() )
+ {
+ if(!isInterfaceSafeForTaggedUnion(superInterfaceDeclRef))
+ return false;
+ }
// If we reach this point then we have a concrete
// witness for each of the case types, and that is
@@ -315,7 +318,7 @@ namespace Slang
{
RefPtr<TaggedUnionSubtypeWitness> taggedUnionWitness = m_astBuilder->create<TaggedUnionSubtypeWitness>();
taggedUnionWitness->sub = taggedUnionType;
- taggedUnionWitness->sup = DeclRefType::create(m_astBuilder, interfaceDeclRef);
+ taggedUnionWitness->sup = DeclRefType::create(m_astBuilder, superTypeDeclRef);
taggedUnionWitness->caseWitnesses.swapWith(caseWitnesses);
*outWitness = taggedUnionWitness;
@@ -327,22 +330,30 @@ namespace Slang
return false;
}
- bool SemanticsVisitor::DoesTypeConformToInterface(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef)
+ bool SemanticsVisitor::isDeclaredSubtype(
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef)
{
- return doesTypeConformToInterfaceImpl(type, type, interfaceDeclRef, nullptr, nullptr);
+ return _isDeclaredSubtype(subType, subType, superTypeDeclRef, nullptr, nullptr);
}
- RefPtr<Val> SemanticsVisitor::tryGetInterfaceConformanceWitness(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef)
+ RefPtr<Val> SemanticsVisitor::tryGetSubtypeWitness(
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef)
{
RefPtr<Val> result;
- doesTypeConformToInterfaceImpl(type, type, interfaceDeclRef, &result, nullptr);
+ _isDeclaredSubtype(subType, subType, superTypeDeclRef, &result, nullptr);
return result;
}
+
+ RefPtr<Val> SemanticsVisitor::tryGetInterfaceConformanceWitness(
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ return tryGetSubtypeWitness(type, interfaceDeclRef);
+ }
+
RefPtr<Val> SemanticsVisitor::createTypeEqualityWitness(
Type* type)
{
diff --git a/source/slang/slang-check-constraint.cpp b/source/slang/slang-check-constraint.cpp
index 427ba9ec2..c37af8892 100644
--- a/source/slang/slang-check-constraint.cpp
+++ b/source/slang/slang-check-constraint.cpp
@@ -80,7 +80,7 @@ namespace Slang
DeclRef<InterfaceDecl> interfaceDeclRef)
{
// The most basic test here should be: does the type declare conformance to the trait.
- if(DoesTypeConformToInterface(type, interfaceDeclRef))
+ if(isDeclaredSubtype(type, interfaceDeclRef))
return type;
// Just because `type` doesn't conform to the given `interfaceDeclRef`, that
@@ -119,7 +119,7 @@ namespace Slang
continue;
// We only want to consider types that implement the target interface.
- if(!DoesTypeConformToInterface(candidateType, interfaceDeclRef))
+ if(!isDeclaredSubtype(candidateType, interfaceDeclRef))
continue;
// We only want to consider types where we can implicitly convert from `type`
diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp
index 1f34e240d..c4e809025 100644
--- a/source/slang/slang-check-conversion.cpp
+++ b/source/slang/slang-check-conversion.cpp
@@ -544,12 +544,12 @@ namespace Slang
if (auto toDeclRefType = as<DeclRefType>(toType))
{
auto toTypeDeclRef = toDeclRefType->declRef;
- if (auto interfaceDeclRef = toTypeDeclRef.as<InterfaceDecl>())
+ if (auto toAggTypeDeclRef = toTypeDeclRef.as<AggTypeDecl>())
{
- if(auto witness = tryGetInterfaceConformanceWitness(fromType, interfaceDeclRef))
+ if(auto witness = tryGetSubtypeWitness(fromType, toAggTypeDeclRef))
{
if (outToExpr)
- *outToExpr = createCastToInterfaceExpr(toType, fromExpr, witness);
+ *outToExpr = createCastToSuperTypeExpr(toType, fromExpr, witness);
if (outCost)
*outCost = kConversionCost_CastToInterface;
return true;
@@ -835,12 +835,12 @@ namespace Slang
return castExpr;
}
- RefPtr<Expr> SemanticsVisitor::createCastToInterfaceExpr(
+ RefPtr<Expr> SemanticsVisitor::createCastToSuperTypeExpr(
RefPtr<Type> toType,
RefPtr<Expr> fromExpr,
RefPtr<Val> witness)
{
- RefPtr<CastToInterfaceExpr> expr = m_astBuilder->create<CastToInterfaceExpr>();
+ RefPtr<CastToSuperTypeExpr> expr = m_astBuilder->create<CastToSuperTypeExpr>();
expr->loc = fromExpr->loc;
expr->type = QualType(toType);
expr->valueArg = fromExpr;
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 66a8ceaf8..8486bf107 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -117,10 +117,24 @@ namespace Slang
void visitInheritanceDecl(InheritanceDecl* inheritanceDecl);
- void visitAggTypeDecl(AggTypeDecl* decl);
+ /// Validate that `decl` isn't illegally inheriting from a type in another module.
+ ///
+ /// This call checks a single `inheritanceDecl` to make sure that it either
+ /// * names a base type from the same module as `decl`, or
+ /// * names a type that allows cross-module inheritance
+ void _validateCrossModuleInheritance(
+ AggTypeDeclBase* decl,
+ InheritanceDecl* inheritanceDecl);
+
+ void visitInterfaceDecl(InterfaceDecl* decl);
+
+ void visitStructDecl(StructDecl* decl);
void visitEnumDecl(EnumDecl* decl);
+ /// Validate that the target type of an extension `decl` is valid.
+ void _validateExtensionDeclTargetType(ExtensionDecl* decl);
+
void visitExtensionDecl(ExtensionDecl* decl);
};
@@ -984,25 +998,10 @@ namespace Slang
base = TranslateTypeNode(base);
inheritanceDecl->base = base;
- // For now we only allow inheritance from interfaces, so
- // we will validate that the type expression names an interface
-
- if(auto declRefType = as<DeclRefType>(base.type))
- {
- if(auto interfaceDeclRef = declRefType->declRef.as<InterfaceDecl>())
- {
- return;
- }
- }
- else if(base.type.is<ErrorType>())
- {
- // If an error was already produced, don't emit a cascading error.
- return;
- }
-
- // If type expression didn't name an interface, we'll emit an error here
- // TODO: deal with the case of an error in the type expression (don't cascade)
- getSink()->diagnose( base.exp, Diagnostics::expectedAnInterfaceGot, base.type);
+ // Note: we do not check whether the type being inherited from
+ // is valid to use for inheritance here, because there could
+ // be contextual factors that need to be taken into account
+ // based on the declaration that is doing the inheriting.
}
// Concretize interface conformances so that we have witnesses as required for lookup.
@@ -1660,6 +1659,12 @@ namespace Slang
inheritanceDecl,
baseInterfaceDeclRef);
}
+ else if( auto structDeclRef = baseTypeDeclRef.as<StructDecl>() )
+ {
+ // The type is saying it inherits from a `struct`,
+ // which doesn't require any checking at present
+ return nullptr;
+ }
}
getSink()->diagnose(inheritanceDecl, Diagnostics::unimplemented, "type not supported for inheritance");
@@ -1761,15 +1766,200 @@ namespace Slang
}
}
- void SemanticsDeclBasesVisitor::visitAggTypeDecl(AggTypeDecl* decl)
+ void SemanticsDeclBasesVisitor::_validateCrossModuleInheritance(
+ AggTypeDeclBase* decl,
+ InheritanceDecl* inheritanceDecl)
{
- // TODO: We need to enumerate the bases here,
- // and ideally form a "class precedence list"
- // from them.
+ // Within a single module, users should be allowed to inherit
+ // one type from another more or less freely, so long as they
+ // don't violate fundamental validity conditions around
+ // inheritance.
+ //
+ // When an inheritance relationship is declared in one module,
+ // and the base type is in another module, we may want to
+ // enforce more restrictions. As a strong example, we probably
+ // don't want people to declare their own subtype of `int`
+ // or `Texture2D<float4>`.
+ //
+ // We start by checking if the type being inherited from is
+ // a decl-ref type, since that means it refers to a declaration
+ // that can be localized to its original module.
+ //
+ auto baseType = inheritanceDecl->base.type;
+ auto baseDeclRefType = as<DeclRefType>(baseType);
+ if( !baseDeclRefType )
+ {
+ return;
+ }
+ auto baseDecl = baseDeclRefType->declRef.decl;
+
+ // Using the parent/child hierarchy baked into `Decl`s we
+ // can find the modules that contain both the `decl` doing
+ // the inheriting, and the `baseDeclRefType` that is being
+ // inherited from.
+ //
+ // If those modules are the same, then we aren't seeing any
+ // kind of cross-module inheritance here, and there is nothing
+ // that needs enforcing.
+ //
+ auto moduleWithInheritance = getModule(decl);
+ auto moduleWithBaseType = getModule(baseDecl);
+ if( moduleWithInheritance == moduleWithBaseType )
+ {
+ return;
+ }
+
+ if( baseDecl->hasModifier<SealedAttribute>() )
+ {
+ // If the original declaration had the `[sealed]` attribute on it,
+ // then it explicitly does *not* allow inheritance from other
+ // modules.
+ //
+ getSink()->diagnose(inheritanceDecl, Diagnostics::cannotInheritFromExplicitlySealedDeclarationInAnotherModule, baseType, moduleWithBaseType->getModuleDecl()->getName());
+ return;
+ }
+ else if( baseDecl->hasModifier<OpenAttribute>() )
+ {
+ // Conversely, if the original declaration had the `[open]` attribute
+ // on it, then it explicit *does* allow inheritance from other
+ // modules.
+ //
+ // In this case we don't need to check anything: the inheritance
+ // is allowed.
+ }
+ else if( as<InterfaceDecl>(baseDecl) )
+ {
+ // If an interface isn't explicitly marked `[open]` or `[sealed]`,
+ // then the default behavior is to treat it as `[open]`, since
+ // interfaces are most often used to define protocols that
+ // users of a module can opt into.
+ }
+ else
+ {
+ // For any non-interface type, if the declaration didn't specify
+ // `[open]` or `[sealed]` then we assume `[sealed]` is the default.
+ //
+ getSink()->diagnose(inheritanceDecl, Diagnostics::cannotInheritFromImplicitlySealedDeclarationInAnotherModule, baseType, moduleWithBaseType->getModuleDecl()->getName());
+ return;
+ }
+ }
+ void SemanticsDeclBasesVisitor::visitInterfaceDecl(InterfaceDecl* decl)
+ {
for( auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>() )
{
ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl);
+ auto baseType = inheritanceDecl->base.type;
+
+ // It is possible that there was an error in checking the base type
+ // expression, and in such a case we shouldn't emit a cascading error.
+ //
+ if( auto baseErrorType = as<ErrorType>(baseType) )
+ {
+ continue;
+ }
+
+ // An `interface` type can only inherit from other `interface` types.
+ //
+ // TODO: In the long run it might make sense for an interface to support
+ // an inheritance clause naming a non-interface type, with the meaning
+ // that any type that implements the interface must be a sub-type of the
+ // type named in the inheritance clause.
+ //
+ auto baseDeclRefType = as<DeclRefType>(baseType);
+ if( !baseDeclRefType )
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfInterfaceMustBeInterface, decl, baseType);
+ continue;
+ }
+
+ auto baseDeclRef = baseDeclRefType->declRef;
+ auto baseInterfaceDeclRef = baseDeclRef.as<InterfaceDecl>();
+ if( !baseInterfaceDeclRef )
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfInterfaceMustBeInterface, decl, baseType);
+ continue;
+ }
+
+ // TODO: At this point we have the `baseInterfaceDeclRef`
+ // and could use it to perform further validity checks,
+ // and/or to build up a more refined representation of
+ // the inheritance graph for this type (e.g., a "class
+ // precedence list").
+ //
+ // E.g., we can/should check that we aren't introducing
+ // a circular inheritance relationship.
+
+ _validateCrossModuleInheritance(decl, inheritanceDecl);
+ }
+ }
+
+ void SemanticsDeclBasesVisitor::visitStructDecl(StructDecl* decl)
+ {
+ // A `struct` type can only inherit from `struct` or `interface` types.
+ //
+ // Furthermore, only the first inheritance clause (in source
+ // order) is allowed to declare a base `struct` type.
+ //
+ Index inheritanceClauseCounter = 0;
+ for( auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>() )
+ {
+ Index inheritanceClauseIndex = inheritanceClauseCounter++;
+
+ ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl);
+ auto baseType = inheritanceDecl->base.type;
+
+ // It is possible that there was an error in checking the base type
+ // expression, and in such a case we shouldn't emit a cascading error.
+ //
+ if( auto baseErrorType = as<ErrorType>(baseType) )
+ {
+ continue;
+ }
+
+ auto baseDeclRefType = as<DeclRefType>(baseType);
+ if( !baseDeclRefType )
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfStructMustBeStructOrInterface, decl, baseType);
+ continue;
+ }
+
+ auto baseDeclRef = baseDeclRefType->declRef;
+ if( auto baseInterfaceDeclRef = baseDeclRef.as<InterfaceDecl>() )
+ {
+ }
+ else if( auto baseStructDeclRef = baseDeclRef.as<StructDecl>() )
+ {
+ // To simplify the task of reading and maintaining code,
+ // we require that when a `struct` inherits from another
+ // `struct`, the base `struct` is the first item in
+ // the list of bases (before any interfaces).
+ //
+ // This constraint also has the secondary effect of restricting
+ // it so that a `struct` cannot multiply inherit from other
+ // `struct` types.
+ //
+ if( inheritanceClauseIndex != 0 )
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseStructMustBeListedFirst, decl, baseType);
+ }
+ }
+ else
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfStructMustBeStructOrInterface, decl, baseType);
+ continue;
+ }
+
+ // TODO: At this point we have the `baseDeclRef`
+ // and could use it to perform further validity checks,
+ // and/or to build up a more refined representation of
+ // the inheritance graph for this type (e.g., a "class
+ // precedence list").
+ //
+ // E.g., we can/should check that we aren't introducing
+ // a circular inheritance relationship.
+
+ _validateCrossModuleInheritance(decl, inheritanceDecl);
}
}
@@ -1795,46 +1985,74 @@ namespace Slang
void SemanticsDeclBasesVisitor::visitEnumDecl(EnumDecl* decl)
{
- // Look at inheritance clauses, and
- // see if one of them is making the enum
- // "inherit" from a concrete type.
- // This will become the "tag" type
- // of the enum.
+ // An `enum` type can inherit from interfaces, and also
+ // from a single "tag" type that must:
+ //
+ // * be a built-in integer type
+ // * come first in the list of base types
+ //
+ Index inheritanceClauseCounter = 0;
RefPtr<Type> tagType;
InheritanceDecl* tagTypeInheritanceDecl = nullptr;
for(auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
{
+ Index inheritanceClauseIndex = inheritanceClauseCounter++;
+
ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl);
+ auto baseType = inheritanceDecl->base.type;
- // Look at the type being inherited from.
- auto superType = inheritanceDecl->base.type;
+ // It is possible that there was an error in checking the base type
+ // expression, and in such a case we shouldn't emit a cascading error.
+ //
+ if( auto baseErrorType = as<ErrorType>(baseType) )
+ {
+ continue;
+ }
- if(auto errorType = as<ErrorType>(superType))
+ auto baseDeclRefType = as<DeclRefType>(baseType);
+ if( !baseDeclRefType )
{
- // Ignore any erroneous inheritance clauses.
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfEnumMustBeIntegerOrInterface, decl, baseType);
continue;
}
- else if(auto declRefType = as<DeclRefType>(superType))
+
+ auto baseDeclRef = baseDeclRefType->declRef;
+ if( auto baseInterfaceDeclRef = baseDeclRef.as<InterfaceDecl>() )
+ {
+ _validateCrossModuleInheritance(decl, inheritanceDecl);
+ }
+ else if( auto baseStructDeclRef = baseDeclRef.as<StructDecl>() )
{
- if(auto interfaceDeclRef = declRefType->declRef.as<InterfaceDecl>())
+ // To simplify the task of reading and maintaining code,
+ // we require that when an `enum` declares an explicit
+ // underlying tag type using an inheritance clause, that
+ // type must be the first item in the list of bases.
+ //
+ // This constraint also has the secondary effect of restricting
+ // it so that an `enum` can't possibly have multiple tag
+ // types declared.
+ //
+ if( inheritanceClauseIndex != 0 )
{
- // Don't consider interface bases as candidates for
- // the tag type.
- continue;
+ getSink()->diagnose(inheritanceDecl, Diagnostics::tagTypeMustBeListedFirst, decl, baseType);
+ }
+ else
+ {
+ tagType = baseType;
+ tagTypeInheritanceDecl = inheritanceDecl;
}
- }
- if(tagType)
- {
- // We already found a tag type.
- getSink()->diagnose(inheritanceDecl, Diagnostics::enumTypeAlreadyHasTagType);
- getSink()->diagnose(tagTypeInheritanceDecl, Diagnostics::seePreviousTagType);
- break;
+ // Note: we do *not* apply the code that validates
+ // cross-module inheritance to a base that represnts
+ // a tag type, because declaring a tag type for an
+ // `enum` doesn't actually make it into a subtype
+ // of the tag type, and thus doesn't violate the
+ // rules when the tag type is `sealed`.
}
else
{
- tagType = superType;
- tagTypeInheritanceDecl = inheritanceDecl;
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfEnumMustBeIntegerOrInterface, decl, baseType);
+ continue;
}
}
@@ -1845,6 +2063,7 @@ namespace Slang
// `enum` types that have a "raw representation" like this from
// ones that are purely abstract and don't expose their
// type of their tag.
+ //
if(!tagType)
{
tagType = m_astBuilder->getIntType();
@@ -2852,10 +3071,8 @@ namespace Slang
}
}
- void SemanticsDeclBasesVisitor::visitExtensionDecl(ExtensionDecl* decl)
+ void SemanticsDeclBasesVisitor::_validateExtensionDeclTargetType(ExtensionDecl* decl)
{
- decl->targetType = CheckProperType(decl->targetType);
-
if (auto targetDeclRefType = as<DeclRefType>(decl->targetType))
{
// Attach our extension to that type as a candidate...
@@ -2867,7 +3084,65 @@ namespace Slang
return;
}
}
- getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here");
+ getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "an 'extension' can only extend a nominal type");
+ }
+
+ void SemanticsDeclBasesVisitor::visitExtensionDecl(ExtensionDecl* decl)
+ {
+ // We check the target type expression, and then validate
+ // that the type it names is one that it makes sense
+ // to extend.
+ //
+ decl->targetType = CheckProperType(decl->targetType);
+ _validateExtensionDeclTargetType(decl);
+
+ for( auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>() )
+ {
+ ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl);
+ auto baseType = inheritanceDecl->base.type;
+
+ // It is possible that there was an error in checking the base type
+ // expression, and in such a case we shouldn't emit a cascading error.
+ //
+ if( auto baseErrorType = as<ErrorType>(baseType) )
+ {
+ continue;
+ }
+
+ // An `extension` can only introduce inheritance from `interface` types.
+ //
+ // TODO: It might in theory make sense to allow an `extension` to
+ // introduce a non-`interface` base if we decide that an `extension`
+ // within the same module as the type it extends counts as just
+ // a continuation of the type's body (like a `partial class` in C#).
+ //
+ auto baseDeclRefType = as<DeclRefType>(baseType);
+ if( !baseDeclRefType )
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfExtensionMustBeInterface, decl, baseType);
+ continue;
+ }
+
+ auto baseDeclRef = baseDeclRefType->declRef;
+ auto baseInterfaceDeclRef = baseDeclRef.as<InterfaceDecl>();
+ if( !baseInterfaceDeclRef )
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfExtensionMustBeInterface, decl, baseType);
+ continue;
+ }
+
+ // TODO: At this point we have the `baseInterfaceDeclRef`
+ // and could use it to perform further validity checks,
+ // and/or to build up a more refined representation of
+ // the inheritance graph for this extension (e.g., a "class
+ // precedence list").
+ //
+ // E.g., we can/should check that we aren't introducing
+ // an inheritance relationship that already existed
+ // on the type as originally declared.
+
+ _validateCrossModuleInheritance(decl, inheritanceDecl);
+ }
}
RefPtr<Type> SemanticsVisitor::calcThisType(DeclRef<Decl> declRef)
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index d8eef571e..c0beb8262 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -325,10 +325,17 @@ namespace Slang
{
// TODO: do we need to make something more
// explicit here?
- bb = ConstructDeclRefExpr(
+ auto expr = ConstructDeclRefExpr(
breadcrumb->declRef,
bb,
loc);
+
+ if(bb && bb->type.isLeftValue)
+ {
+ expr->type.isLeftValue = true;
+ }
+
+ bb = expr;
}
break;
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 42edb5df7..c316dd820 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -645,7 +645,7 @@ namespace Slang
/// which packages up the value, its type, and the witness
/// of its conformance to the interface.
///
- RefPtr<Expr> createCastToInterfaceExpr(
+ RefPtr<Expr> createCastToSuperTypeExpr(
RefPtr<Type> toType,
RefPtr<Expr> fromExpr,
RefPtr<Val> witness);
@@ -924,9 +924,15 @@ namespace Slang
RefPtr<DeclaredSubtypeWitness> createSimpleSubtypeWitness(
TypeWitnessBreadcrumb* breadcrumb);
+ /// Create a withness that `subType` is a sub-type of `superTypeDeclRef`.
+ ///
+ /// The `inBreadcrumbs` parameter represents a linked list of steps
+ /// in the process that validated the sub-type relationship, which
+ /// will be used to inform the construction of the witness.
+ ///
RefPtr<Val> createTypeWitness(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef,
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef,
TypeWitnessBreadcrumb* inBreadcrumbs);
/// Is the given interface one that a tagged-union type can conform to?
@@ -950,20 +956,48 @@ namespace Slang
DeclRef<InterfaceDecl> interfaceDeclRef,
DeclRef<Decl> requirementDeclRef);
- bool doesTypeConformToInterfaceImpl(
- RefPtr<Type> originalType,
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef,
+ /// Check whether `subType` is declared a sub-type of `superTypeDeclRef`
+ ///
+ /// If this function returns `true` (because the subtype relationship holds),
+ /// then `outWitness` will be set to a value that serves as a witness
+ /// to the subtype relationship.
+ ///
+ /// This function may be used to validate a transitive subtype relationship
+ /// where, e.g., `A : C` becase `A : B` and `B : C`. In such a case, a recursive
+ /// call to `_isDeclaredSubtype` may occur where `originalSubType` is `A`,
+ /// `subType` is `C`, and `superTypeDeclRef` is `C`. The `inBreadcrumbs` in that
+ /// case would include information for the `A : B` relationship, which can be
+ /// used to construct a witness for `A : C` from the `A : B` and `B : C` witnesses.
+ ///
+ bool _isDeclaredSubtype(
+ RefPtr<Type> originalSubType,
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef,
RefPtr<Val>* outWitness,
TypeWitnessBreadcrumb* inBreadcrumbs);
- bool DoesTypeConformToInterface(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef);
+ /// Check whether `subType` is a sub-type of `superTypeDeclRef`.
+ bool isDeclaredSubtype(
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef);
+
+ /// Check whether `subType` is a sub-type of `superTypeDeclRef`,
+ /// and return a witness to the sub-type relationship if it holds
+ /// (return null otherwise).
+ ///
+ RefPtr<Val> tryGetSubtypeWitness(
+ RefPtr<Type> subType,
+ DeclRef<AggTypeDecl> superTypeDeclRef);
+ /// Check whether `type` conforms to `interfaceDeclRef`,
+ /// and return a witness to the conformance if it holds
+ /// (return null otherwise).
+ ///
+ /// This function is equivalent to `tryGetSubtypeWitness()`.
+ ///
RefPtr<Val> tryGetInterfaceConformanceWitness(
- RefPtr<Type> type,
- DeclRef<InterfaceDecl> interfaceDeclRef);
+ RefPtr<Type> type,
+ DeclRef<InterfaceDecl> interfaceDeclRef);
/// Does there exist an implicit conversion from `fromType` to `toType`?
bool canConvertImplicitly(
@@ -1381,7 +1415,7 @@ namespace Slang
CASE(OverloadedExpr)
CASE(OverloadedExpr2)
CASE(AggTypeCtorExpr)
- CASE(CastToInterfaceExpr)
+ CASE(CastToSuperTypeExpr)
CASE(LetExpr)
CASE(ExtractExistentialValueExpr)
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 6e77aa45d..41c674ddb 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -289,8 +289,6 @@ DIAGNOSTIC(31120, Error, invalidAttributeTarget, "invalid syntax target for user
// Enums
DIAGNOSTIC(32000, Error, invalidEnumTagType, "invalid tag type for 'enum': '$0'")
-DIAGNOSTIC(32001, Error, enumTypeAlreadyHasTagType, "'enum' type has already declared a tag type")
-DIAGNOSTIC(32002, Note, seePreviousTagType, "see previous tag type declaration")
DIAGNOSTIC(32003, Error, unexpectedEnumTagExpr, "unexpected form for 'enum' tag value expression")
@@ -320,6 +318,18 @@ DIAGNOSTIC(30610, Error, ambiguousDefaultInitializerForType, "more than one defa
// 307xx: parameters
DIAGNOSTIC(30700, Error, outputParameterCannotHaveDefaultValue, "an 'out' or 'inout' parameter cannot have a default-value expression");
+// 308xx: inheritance
+DIAGNOSTIC(30810, Error, baseOfInterfaceMustBeInterface, "interface '$0' cannot inherit from non-interface type '$1'")
+DIAGNOSTIC(30811, Error, baseOfStructMustBeStructOrInterface, "struct '$0' cannot inherit from type '$1' that is neither a struct nor an interface")
+DIAGNOSTIC(30812, Error, baseOfEnumMustBeIntegerOrInterface, "enum '$0' cannot inherit from type '$1' that is neither an interface not a builtin integer type")
+DIAGNOSTIC(30810, Error, baseOfExtensionMustBeInterface, "extension cannot inherit from non-interface type '$1'")
+
+DIAGNOSTIC(30820, Error, baseStructMustBeListedFirst, "a struct type may only inherit from one other struct type, and that type must appear first in the list of bases")
+DIAGNOSTIC(30821, Error, tagTypeMustBeListedFirst, "an unum type may only have a single tag type, and that type must be listed first in the list of bases")
+
+DIAGNOSTIC(30820, Error, cannotInheritFromExplicitlySealedDeclarationInAnotherModule, "cannot inherit from type '$0' marked 'sealed' in module '$1'")
+DIAGNOSTIC(30821, Error, cannotInheritFromImplicitlySealedDeclarationInAnotherModule, "cannot inherit from type '$0' in module '$1' because it is implicitly 'sealed'; mark the base type 'open' to allow inheritance across modules")
+
// 39999 waiting to be placed in the right range
DIAGNOSTIC(39999, Error, expectedIntegerConstantWrongType, "expected integer constant (found: '$0')")
@@ -343,8 +353,6 @@ DIAGNOSTIC(39999, Error, expectedAGeneric, "expected a generic when using '<...>
DIAGNOSTIC(39999, Error, genericArgumentInferenceFailed, "could not specialize generic for arguments of type $0")
DIAGNOSTIC(39999, Note, genericSignatureTried, "see declaration of $0")
-DIAGNOSTIC(39999, Error, expectedAnInterfaceGot, "expected an interface, got '$0'")
-
DIAGNOSTIC(39999, Error, ambiguousReference, "ambiguous reference to '$0'");
DIAGNOSTIC(39999, Error, ambiguousExpression, "ambiguous reference");
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index f369729d2..c52025244 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -701,17 +701,17 @@ LoweredValInfo emitCallToDeclRef(
}
IRInst* getFieldKey(
- IRGenContext* context,
- DeclRef<VarDecl> field)
+ IRGenContext* context,
+ DeclRef<Decl> field)
{
return getSimpleVal(context, emitDeclRef(context, field, context->irBuilder->getKeyType()));
}
LoweredValInfo extractField(
- IRGenContext* context,
- IRType* fieldType,
- LoweredValInfo base,
- DeclRef<VarDecl> field)
+ IRGenContext* context,
+ IRType* fieldType,
+ LoweredValInfo base,
+ DeclRef<Decl> field)
{
IRBuilder* builder = context->irBuilder;
@@ -2054,6 +2054,21 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
}
else if(auto constraintDeclRef = declRef.as<TypeConstraintDecl>())
{
+ auto superType = getSup(getASTBuilder(), constraintDeclRef);
+ if(auto superDeclRefType = as<DeclRefType>(superType))
+ {
+ if(auto superStructDeclRef = superDeclRefType->declRef.template as<StructDecl>())
+ {
+ // The constraint is saying that the given type inherits
+ // from a concrete `struct` type, which means it should
+ // be satisfied by a witness that represents a field
+ // (TODO: or a chain of fields) to fetch to get the
+ // final value.
+ //
+ return extractField(loweredType, loweredBase, constraintDeclRef);
+ }
+ }
+
// The code is making use of a "witness" that a value of
// some generic type conforms to an interface.
//
@@ -2748,30 +2763,87 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
UNREACHABLE_RETURN(LoweredValInfo());
}
- LoweredValInfo visitCastToInterfaceExpr(
- CastToInterfaceExpr* expr)
+ /// Emit code to cast `value` to a concrete `superType` (e.g., a `struct`).
+ ///
+ /// The `subTypeWitness` is expected to witness the sub-type relationship
+ /// by naming a field (or chain of fields) that leads from the type of
+ /// `value` to the field that stores its members for `superType`.
+ ///
+ LoweredValInfo emitCastToConcreteSuperTypeRec(
+ LoweredValInfo const& value,
+ IRType* superType,
+ Val* subTypeWitness)
{
- // We have an expression that is "up-casting" some concrete value
- // to an existential type (aka interface type), using a subtype witness
- // (which will lower as a witness table) to show that the conversion
- // is valid.
- //
- // At the IR level, this will become a `makeExistential` instruction,
- // which collects the above information into a single IR-level value.
- // A dynamic CPU implementation of Slang might encode an existential
- // as a "fat pointer" representation, which includes a pointer to
- // data for the concrete value, plus a pointer to the witness table.
+ if( auto declaredSubtypeWitness = as<DeclaredSubtypeWitness>(subTypeWitness) )
+ {
+ return extractField(superType, value, declaredSubtypeWitness->declRef);
+ }
+ else
+ {
+ SLANG_ASSERT(!"unhandled");
+ return nullptr;
+ }
+ }
+
+ LoweredValInfo visitCastToSuperTypeExpr(
+ CastToSuperTypeExpr* expr)
+ {
+ auto superType = lowerType(context, expr->type);
+ auto value = lowerRValueExpr(context, expr->valueArg);
+
+ // The actual operation that we need to perform here
+ // depends on the kind of subtype relationship we
+ // are making use of.
//
- // Note: if/when Slang supports more general existential types, such
- // as compositions of interface (e.g., `IReadable & IWritable`), then
- // we should probably extend the AST and IR mechanism here to accept
- // a sequence of witness tables.
+ // The first important case is when the super type is
+ // an interface type, such that casting from a concrete
+ // value to that type creates a value of existential
+ // type that binds together the concrete value and the
+ // witness table that represents the subtype relationship.
//
- auto existentialType = lowerType(context, expr->type);
- auto concreteValue = getSimpleVal(context, lowerRValueExpr(context, expr->valueArg));
- auto witnessTable = lowerSimpleVal(context, expr->witnessArg);
- auto existentialValue = getBuilder()->emitMakeExistential(existentialType, concreteValue, witnessTable);
- return LoweredValInfo::simple(existentialValue);
+ if( auto declRefType = as<DeclRefType>(expr->type) )
+ {
+ auto declRef = declRefType->declRef;
+ if( auto interfaceDeclRef = declRef.as<InterfaceDecl>() )
+ {
+ // We have an expression that is "up-casting" some concrete value
+ // to an existential type (aka interface type), using a subtype witness
+ // (which will lower as a witness table) to show that the conversion
+ // is valid.
+ //
+ auto witnessTable = lowerSimpleVal(context, expr->witnessArg);
+
+ // At the IR level, this will become a `makeExistential` instruction,
+ // which collects the above information into a single IR-level value.
+ // A dynamic CPU implementation of Slang might encode an existential
+ // as a "fat pointer" representation, which includes a pointer to
+ // data for the concrete value, plus a pointer to the witness table.
+ //
+ // Note: if/when Slang supports more general existential types, such
+ // as compositions of interface (e.g., `IReadable & IWritable`), then
+ // we should probably extend the AST and IR mechanism here to accept
+ // a sequence of witness tables.
+ //
+ auto concreteValue = getSimpleVal(context, value);
+ auto existentialValue = getBuilder()->emitMakeExistential(
+ superType,
+ concreteValue,
+ witnessTable);
+ return LoweredValInfo::simple(existentialValue);
+ }
+ else if( auto structDeclRef = declRef.as<StructDecl>() )
+ {
+ // We are up-casting to a concrete `struct` super-type,
+ // such that the witness will represent a field of the super-type
+ // that is stored in instances of the sub-type (or a chain
+ // of such fields for a transitive witness).
+ //
+ return emitCastToConcreteSuperTypeRec(value, superType, expr->witnessArg);
+ }
+ }
+
+ SLANG_UNEXPECTED("unexpected case of subtype relationship");
+ UNREACHABLE_RETURN(LoweredValInfo());
}
LoweredValInfo subscriptValue(
@@ -2815,9 +2887,9 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo>
}
LoweredValInfo extractField(
- IRType* fieldType,
- LoweredValInfo base,
- DeclRef<VarDecl> field)
+ IRType* fieldType,
+ LoweredValInfo base,
+ DeclRef<Decl> field)
{
return Slang::extractField(context, fieldType, base, field);
}
@@ -4524,6 +4596,21 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
// What is the super-type that we have declared we inherit from?
RefPtr<Type> superType = inheritanceDecl->base.type;
+ if(auto superDeclRefType = as<DeclRefType>(superType))
+ {
+ if( auto superStructDeclRef = superDeclRefType->declRef.as<StructDecl>() )
+ {
+ // TODO: the witness that a type inherits from a `struct`
+ // type should probably be a key that will be used for
+ // a field that holds the base type...
+ //
+ auto irKey = getBuilder()->createStructKey();
+ auto keyVal = LoweredValInfo::simple(irKey);
+ setGlobalValue(context, inheritanceDecl, keyVal);
+ return keyVal;
+ }
+ }
+
// Construct the mangled name for the witness table, which depends
// on the type that is conforming, and the type that it conforms to.
//
@@ -5270,6 +5357,27 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
subBuilder->setInsertInto(irStruct);
+ // A `struct` that inherits from another `struct` must start
+ // with a member for the direct base type.
+ //
+ for( auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>() )
+ {
+ auto superType = inheritanceDecl->base;
+ if(auto superDeclRefType = as<DeclRefType>(superType))
+ {
+ if(auto superStructDeclRef = superDeclRefType->declRef.as<StructDecl>())
+ {
+ auto superKey = (IRStructKey*) getSimpleVal(context, ensureDecl(context, inheritanceDecl));
+ auto irSuperType = lowerType(context, superType.type);
+ subBuilder->createStructField(
+ irStruct,
+ superKey,
+ irSuperType);
+ }
+ }
+ }
+
+
for (auto fieldDecl : decl->getMembersOfType<VarDeclBase>())
{
if (fieldDecl->hasModifier<HLSLStaticModifier>())