summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-check-conformance.cpp
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/slang-check-conformance.cpp
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/slang-check-conformance.cpp')
-rw-r--r--source/slang/slang-check-conformance.cpp71
1 files changed, 41 insertions, 30 deletions
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)
{