diff options
Diffstat (limited to 'source/slang/check.cpp')
| -rw-r--r-- | source/slang/check.cpp | 163 |
1 files changed, 158 insertions, 5 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 2fa4e9bc7..9aa5eb689 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -9,6 +9,8 @@ namespace Slang { + RefPtr<TypeType> getTypeType( + Type* type); /// Should the given `decl` nested in `parentDecl` be treated as a static rather than instance declaration? bool isEffectivelyStatic( @@ -677,7 +679,7 @@ namespace Slang auto baseExprType = baseExpr->type.type; RefPtr<SharedTypeExpr> baseTypeExpr = new SharedTypeExpr(); baseTypeExpr->base.type = baseExprType; - baseTypeExpr->type = new TypeType(baseExprType); + baseTypeExpr->type.type = getTypeType(baseExprType); auto expr = new StaticMemberExpr(); expr->loc = loc; @@ -2071,9 +2073,7 @@ namespace Slang { RefPtr<TypeCastExpr> castExpr = createImplicitCastExpr(); - auto typeType = new TypeType(); - typeType->setSession(getSession()); - typeType->type = toType; + auto typeType = getTypeType(toType); auto typeExpr = new SharedTypeExpr(); typeExpr->type.type = typeType; @@ -5547,6 +5547,60 @@ namespace Slang return witness; } + /// Is the given interface one that a tagged-union type can conform to? + /// + /// If a tagged union type `__TaggedUnion(A,B)` is going to be + /// plugged in for a type parameter `T : IFoo` then we need to + /// be sure that the interface `IFoo` doesn't have anything + /// that could lead to unsafe/unsound behavior. This function + /// checks that all the requirements on the interfaceare safe ones. + /// + bool isInterfaceSafeForTaggedUnion( + DeclRef<InterfaceDecl> interfaceDeclRef) + { + for( auto memberDeclRef : getMembers(interfaceDeclRef) ) + { + if(!isInterfaceRequirementSafeForTaggedUnion(interfaceDeclRef, memberDeclRef)) + return false; + } + + return true; + } + + /// Is the given interface requirement one that a tagged-union type can satisfy? + /// + /// Unsafe requirements include any `static` requirements, + /// any associated types, and also any requirements that make + /// use of the `This` type (once we support it). + /// + bool isInterfaceRequirementSafeForTaggedUnion( + DeclRef<InterfaceDecl> interfaceDeclRef, + DeclRef<Decl> requirementDeclRef) + { + if(auto callableDeclRef = requirementDeclRef.As<CallableDecl>()) + { + // A `static` method requirement can't be satisfied by a + // tagged union, because there is no tag to dispatch on. + // + if(requirementDeclRef.getDecl()->HasModifier<HLSLStaticModifier>()) + return false; + + // TODO: We will eventually want to check that any callable + // requirements do not use the `This` type or any associated + // types in ways that could lead to errors. + // + // For now we are disallowing interfaces that have associated + // types completely, and we haven't implemented the `This` + // type, so we should be safe. + + return true; + } + else + { + return false; + } + } + bool doesTypeConformToInterfaceImpl( RefPtr<Type> originalType, RefPtr<Type> type, @@ -5661,6 +5715,69 @@ namespace Slang } } } + else if(auto taggedUnionType = type->As<TaggedUnionType>()) + { + // A tagged union type conforms to an interface if all of + // the constituent types in the tagged union conform. + // + // We will iterate over the "case" types in the tagged + // union, and check if they conform to the interface. + // Along the way we will collect the conformance witness + // values *if* we are being asked to produce a witness + // value for the tagged union itself (that is, if + // `outWitness` is non-null). + // + List<RefPtr<Val>> caseWitnesses; + for(auto caseType : taggedUnionType->caseTypes) + { + RefPtr<Val> caseWitness; + + if(!doesTypeConformToInterfaceImpl( + caseType, + caseType, + interfaceDeclRef, + outWitness ? &caseWitness : nullptr, + nullptr)) + { + return false; + } + + if(outWitness) + { + caseWitnesses.Add(caseWitness); + } + } + + // We also need to validate the requirements on + // the interface to make sure that they are suitable for + // use with a tagged-union type. + // + // For example, if the interface includes a `static` method + // (which can therefore be called without a particular instance), + // then we wouldn't know what implementation of that method + // to use because there is no tag value to dispatch on. + // + // We will start out being conservative about what we accept + // here, just to keep things simple. + // + if(!isInterfaceSafeForTaggedUnion(interfaceDeclRef)) + return false; + + // If we reach this point then we have a concrete + // witness for each of the case types, and that is + // enough to build a witness for the tagged union. + // + if(outWitness) + { + RefPtr<TaggedUnionSubtypeWitness> taggedUnionWitness = new TaggedUnionSubtypeWitness(); + taggedUnionWitness->sub = taggedUnionType; + taggedUnionWitness->sup = DeclRefType::Create(getSession(), interfaceDeclRef); + taggedUnionWitness->caseWitnesses.SwapWith(caseWitnesses); + + *outWitness = taggedUnionWitness; + } + return true; + } // default is failure return false; @@ -8090,6 +8207,23 @@ namespace Slang return expr; } + RefPtr<Expr> visitTaggedUnionTypeExpr(TaggedUnionTypeExpr* expr) + { + // We have an expression of the form `__TaggedUnion(A, B, ...)` + // which will evaluate to a tagged-union type over `A`, `B`, etc. + // + RefPtr<TaggedUnionType> type = new TaggedUnionType(); + expr->type = QualType(getTypeType(type)); + + for( auto& caseTypeExpr : expr->caseTypes ) + { + caseTypeExpr = CheckProperType(caseTypeExpr); + type->caseTypes.Add(caseTypeExpr.type); + } + + return expr; + } + @@ -9039,7 +9173,7 @@ namespace Slang scopesToTry.Add(module->moduleDecl->scope); List<RefPtr<Type>> globalGenericArgs; - for (auto name : entryPoint->genericParameterTypeNames) + for (auto name : entryPoint->genericArgStrings) { // parse type name RefPtr<Type> type; @@ -9059,6 +9193,25 @@ namespace Slang return; } + // The following is a bit of a hack. + // + // Back-end code generation relies on us having computed layouts for all tagged + // unions that end up being used in the code, which means we need a way to find + // all such types that get used in a module (and the stuff it imports). + // + // The Right Way to handle this would probably be to have each `ModuleDecl` track + // any tagged union types that get created in the context of that module, and + // then combine those lists later. + // + // For now we are assuming a tagged union type only comes into existence + // as a (top-level) argument for a generic type parameter, so that we + // can check for them here and cache them on the entry point request. + // + if( auto taggedUnionType = type->As<TaggedUnionType>() ) + { + entryPoint->taggedUnionTypes.Add(taggedUnionType); + } + globalGenericArgs.Add(type); } |
