diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-ast-decl.h | 23 | ||||
| -rw-r--r-- | source/slang/slang-ast-iterator.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 586 | ||||
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 7 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 21 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts-stable-names.lua | 1 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.lua | 6 | ||||
| -rw-r--r-- | source/slang/slang-ir-link.cpp | 7 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-language-server-ast-lookup.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 71 | ||||
| -rw-r--r-- | source/slang/slang-parameter-binding.cpp | 675 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-syntax.h | 5 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.cpp | 65 | ||||
| -rw-r--r-- | source/slang/slang-type-layout.h | 6 |
18 files changed, 620 insertions, 867 deletions
diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h index 40c55ec44..1a850da0d 100644 --- a/source/slang/slang-ast-decl.h +++ b/source/slang/slang-ast-decl.h @@ -386,8 +386,16 @@ class AggTypeDecl : public AggTypeDeclBase FIDDLE(...) FIDDLE() TypeTag typeTags = TypeTag::None; - // Used if this type declaration is a wrapper, i.e. struct FooWrapper:IFoo = Foo; - TypeExp wrappedType; + // When user defines an agg type in the syntax of + // `struct FooAlias : IFoo = Foo;` + // The user is defining a link-time type alias. In contrast + // to an ordinary typealias, a link-time alias is not folded in + // the front-end, and resolved during linking. + // `aliasedType` is used to store the alised type (in this case `Foo`) + // when the agg type decl is declared in the link-time alias syntax. + // + TypeExp aliasedType; + bool hasBody = true; void unionTagsWith(TypeTag other); @@ -506,6 +514,13 @@ class InheritanceDecl : public TypeConstraintDecl // this inheritance declaration. FIDDLE() RefPtr<WitnessTable> witnessTable; + // If the inheritance decl is in a link-time type declaration + // (e.g. `export struct Foo : IFoo = FooImpl;`), then we will + // store the witness that `FooImpl:IFoo` here. + // TODO: If we made `WitnessTable` a `Val`, we should be able + // to unify these two cases. + FIDDLE() Witness* witnessVal = nullptr; + // Overrides should be public so base classes can access const TypeExp& _getSupOverride() const { return base; } }; @@ -1015,4 +1030,8 @@ void addSiblingScopeForContainerDecl( ContainerDecl* source); void addSiblingScopeForContainerDecl(ASTBuilder* builder, Scope* destScope, ContainerDecl* source); +// Cast `decl` to a valid `ContainerDecl*` if its members will become global scope symbols after +// lowering to IR. This currently includes: `NamespaceDecl`, `ModuleDecl` and `FileDecl`. +ContainerDecl* isStaticScopeDecl(Decl* decl); + } // namespace Slang diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h index 379de0560..dfb203153 100644 --- a/source/slang/slang-ast-iterator.h +++ b/source/slang/slang-ast-iterator.h @@ -543,7 +543,7 @@ void ASTIterator<CallbackFunc, FilterFunc>::visitDecl(DeclBase* decl) visitDecl(member); } if (auto aggTypeDecl = as<AggTypeDecl>(decl)) - visitExpr(aggTypeDecl->wrappedType.exp); + visitExpr(aggTypeDecl->aliasedType.exp); } for (auto modifier : decl->modifiers) { diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 9dd481acb..3ccbd4ff7 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -1260,6 +1260,7 @@ FIDDLE() namespace Slang Type* Ptr() { return type; } operator Type*() { return type; } Type* operator->() { return Ptr(); } + explicit operator bool() const { return type != nullptr; } ThisType& operator=(const ThisType& rhs) = default; diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 6a4e3668f..fa31c54bd 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -2553,24 +2553,17 @@ void SemanticsDeclHeaderVisitor::visitStructDecl(StructDecl* structDecl) structDecl->addTag(TypeTag::Incomplete); } - // Slang supports a convenient syntax to create a wrapper type from + // Slang supports a convenient syntax to create a link-time aliased type from // an existing type that implements a given interface. For example, // the user can write: struct FooWrapper:IFoo = Foo; - // In this case we will synthesize the FooWrapper type with an inner - // member of type `Foo`, and use it to implement all requirements of - // IFoo. - // If this is a wrapper struct, synthesize the inner member now. - if (structDecl->wrappedType.exp) - { - structDecl->wrappedType = CheckProperType(structDecl->wrappedType); - auto member = m_astBuilder->create<VarDecl>(); - member->type = structDecl->wrappedType; - member->nameAndLoc.name = getName("inner"); - member->nameAndLoc.loc = structDecl->wrappedType.exp->loc; - member->loc = member->nameAndLoc.loc; - addModifier(member, m_astBuilder->create<SynthesizedModifier>()); - structDecl->addMember(member); + // In this case we need to check the aliasedType expr. + if (structDecl->aliasedType.exp) + { + SemanticsVisitor visitor(withDeclToExcludeFromLookup(structDecl)); + structDecl->aliasedType = visitor.CheckProperType(structDecl->aliasedType); + structDecl->addTag(getTypeTags(structDecl->aliasedType)); } + checkVisibility(structDecl); } @@ -5348,16 +5341,6 @@ void SemanticsVisitor::_addMethodWitness( witnessTable->add(requiredMemberDeclRef.getDecl(), RequirementWitness(satisfyingMemberDeclRef)); } -static bool isWrapperTypeDecl(Decl* decl) -{ - if (auto aggTypeDecl = as<AggTypeDecl>(decl)) - { - if (aggTypeDecl->wrappedType) - return true; - } - return false; -} - // Is it allowed to have an interface method parameter whose direction is `reqDir`, and an // implementing method parameter whose direction is `implDir`? // @@ -5487,16 +5470,14 @@ bool SemanticsVisitor::trySynthesizeMethodRequirementWitness( // With the big picture spelled out, we can settle into // the work of constructing our synthesized method. // - bool isInWrapperType = isWrapperTypeDecl(context->parentDecl); - // First, we check that the differentiabliity of the method matches the requirement, // and we don't attempt to synthesize a method if they don't match. if (lookupResult.isValid()) { - if (!isInWrapperType && getShared()->getFuncDifferentiableLevel( - as<FunctionDeclBase>(lookupResult.item.declRef.getDecl())) < - getShared()->getFuncDifferentiableLevel( - as<FunctionDeclBase>(requiredMemberDeclRef.getDecl()))) + if (getShared()->getFuncDifferentiableLevel( + as<FunctionDeclBase>(lookupResult.item.declRef.getDecl())) < + getShared()->getFuncDifferentiableLevel( + as<FunctionDeclBase>(requiredMemberDeclRef.getDecl()))) { return false; } @@ -5524,25 +5505,7 @@ bool SemanticsVisitor::trySynthesizeMethodRequirementWitness( auto baseOverloadedExpr = m_astBuilder->create<OverloadedExpr>(); baseOverloadedExpr->name = requiredMemberDeclRef.getDecl()->getName(); - if (isInWrapperType) - { - auto aggTypeDecl = as<AggTypeDecl>(context->parentDecl); - baseOverloadedExpr->lookupResult2 = lookUpMember( - m_astBuilder, - this, - baseOverloadedExpr->name, - aggTypeDecl->wrappedType.type, - aggTypeDecl->ownedScope, - LookupMask::Default, - LookupOptions::IgnoreBaseInterfaces); - addModifier(synFuncDecl, m_astBuilder->create<ForceInlineAttribute>()); - - synFuncDecl->parentDecl = aggTypeDecl; - } - else - { - baseOverloadedExpr->lookupResult2 = lookupResult; - } + baseOverloadedExpr->lookupResult2 = lookupResult; // Non-static methods cannot implement static methods, remove them. if (requiredMemberDeclRef.getDecl()->hasModifier<HLSLStaticModifier>()) @@ -5557,24 +5520,7 @@ bool SemanticsVisitor::trySynthesizeMethodRequirementWitness( // if (synThis) { - if (isInWrapperType) - { - // If this is a wrapper type, then use the inner - // object as the actual this parameter for the redirected - // call. - auto innerExpr = m_astBuilder->create<VarExpr>(); - innerExpr->scope = synThis->scope; - innerExpr->name = getName("inner"); - baseOverloadedExpr->base = CheckExpr(innerExpr); - SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(synFuncDecl)); - bodyVisitor.maybeRegisterDifferentiableType( - m_astBuilder, - baseOverloadedExpr->base->type); - } - else - { - baseOverloadedExpr->base = synThis; - } + baseOverloadedExpr->base = synThis; } @@ -5823,8 +5769,7 @@ bool SemanticsVisitor::trySynthesizeConstructorRequirementWitness( bool isDefaultInitializableType = requiredMemberDeclRef.getParent() == getASTBuilder()->getDefaultInitializableTypeInterfaceDecl(); - bool isInWrapperType = isWrapperTypeDecl(context->parentDecl); - if (!isInWrapperType && !isDefaultInitializableType && !satisfyingMemberLookupResult.isValid()) + if (!isDefaultInitializableType && !satisfyingMemberLookupResult.isValid()) { return false; } @@ -5846,53 +5791,7 @@ bool SemanticsVisitor::trySynthesizeConstructorRequirementWitness( auto seqStmt = m_astBuilder->create<SeqStmt>(); ctorDecl->body = seqStmt; - - if (isInWrapperType) - { - SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl)); - bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType); - - if (auto varDecl = context->parentDecl->findFirstDirectMemberDeclOfType<VarDeclBase>()) - { - auto varExpr = m_astBuilder->create<VarExpr>(); - varExpr->scope = ctorDecl->ownedScope; - varExpr->name = varDecl->getName(); - auto checkedVarExpr = CheckTerm(varExpr); - if (!checkedVarExpr) - return false; - if (as<ErrorType>(checkedVarExpr->type.type)) - return false; - auto assign = m_astBuilder->create<AssignExpr>(); - assign->left = checkedVarExpr; - auto temp = m_astBuilder->create<InvokeExpr>(); - auto lookupResult = lookUpMember( - m_astBuilder, - this, - ctorName, - varDecl->type.type, - ctorDecl->ownedScope, - LookupMask::Function, - LookupOptions::IgnoreBaseInterfaces); - temp->functionExpr = createLookupResultExpr( - ctorName, - lookupResult, - nullptr, - context->parentDecl->loc, - nullptr); - temp->arguments.addRange(synArgs); - auto resolvedVar = ResolveInvoke(temp); - if (!resolvedVar) - return false; - assign->right = resolvedVar; - assign->type = m_astBuilder->getVoidType(); - bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type); - - auto stmt = m_astBuilder->create<ExpressionStmt>(); - stmt->expression = assign; - seqStmt->stmts.add(stmt); - } - } - else if (synArgs.getCount()) + if (synArgs.getCount()) { // The body of our synthesized method is going to try to // make a ctor call with the specified arguments (e.g., @@ -5965,12 +5864,6 @@ bool SemanticsVisitor::trySynthesizePropertyRequirementWitness( DeclRef<PropertyDecl> requiredMemberDeclRef, RefPtr<WitnessTable> witnessTable) { - if (isWrapperTypeDecl(context->parentDecl)) - return trySynthesizeWrapperTypePropertyRequirementWitness( - context, - requiredMemberDeclRef, - witnessTable); - // The situation here is that the context of an inheritance // declaration didn't provide an exact match for a required // property. E.g.: @@ -6130,244 +6023,6 @@ bool SemanticsVisitor::trySynthesizePropertyRequirementWitness( return true; } -bool SemanticsVisitor::trySynthesizeWrapperTypePropertyRequirementWitness( - ConformanceCheckingContext* context, - DeclRef<PropertyDecl> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable) -{ - // We are synthesizing a property requirement for a wrapper type: - // - // interface IFoo { property value : int { get; set; } } - // struct Foo : IFoo = FooImpl; - // - // We need to synthesize Foo to: - // - // struct Foo : IFoo - // { - // FooImpl inner; - // property value : int { get { return inner.value; } - // set { inner.value = newValue; } - // } - // } - // - // To do so, we need to grab the witness table of FooImpl:IFoo, and create - // wrapper property in Foo that forwards the accessors to the inner object. - // - // We get started by constructing a synthesized `PropertyDecl`. - // - auto synPropertyDecl = m_astBuilder->create<PropertyDecl>(); - synPropertyDecl->parentDecl = context->parentDecl; - - // Synthesize the property name with a prefix to avoid name clashing. - // - synPropertyDecl->nameAndLoc = requiredMemberDeclRef.getDecl()->nameAndLoc; - synPropertyDecl->nameAndLoc.name = - getName(String("$syn_property_") + getText(requiredMemberDeclRef.getName())); - - // Find the witness that FooImpl : IFoo. - auto aggTypeDecl = as<AggTypeDecl>(context->parentDecl); - auto innerType = aggTypeDecl->wrappedType.type; - DeclRef<Decl> innerProperty; - auto innerWitness = tryGetSubtypeWitness(innerType, witnessTable->baseType); - if (!innerWitness) - return false; - - for (auto requiredAccessorDeclRef : - getMembersOfType<AccessorDecl>(m_astBuilder, requiredMemberDeclRef)) - { - auto innerEntry = tryLookUpRequirementWitness( - m_astBuilder, - innerWitness, - requiredAccessorDeclRef.getDecl()); - if (innerEntry.getFlavor() != RequirementWitness::Flavor::declRef) - return false; - auto innerAccessorDeclRef = as<AccessorDecl>(innerEntry.getDeclRef()); - if (!innerAccessorDeclRef) - return false; - - // The synthesized accessor will be an AST node of the same class as - // the required accessor. - // - auto synAccessorDecl = (AccessorDecl*)m_astBuilder->createByNodeType( - requiredAccessorDeclRef.getDecl()->astNodeType); - synAccessorDecl->ownedScope = m_astBuilder->create<Scope>(); - synAccessorDecl->ownedScope->containerDecl = synAccessorDecl; - synAccessorDecl->ownedScope->parent = getScope(context->parentDecl); - - // The return type should be the same as the inner object's accessor return type. - // - synAccessorDecl->returnType.type = getResultType(m_astBuilder, innerAccessorDeclRef); - - // Similarly, our synthesized accessor will have parameters matching those of the inner - // accessor. - // - List<Expr*> synArgs; - for (auto innerParamDeclRef : getParameters(m_astBuilder, innerAccessorDeclRef)) - { - auto paramType = getType(m_astBuilder, innerParamDeclRef); - - // The synthesized parameter will ahve the same name and - // type as the parameter of the requirement. - // - auto synParamDecl = m_astBuilder->create<ParamDecl>(); - synParamDecl->nameAndLoc = innerParamDeclRef.getDecl()->nameAndLoc; - synParamDecl->type.type = paramType; - - // We need to add the parameter as a child declaration of - // the accessor we are building. - // - synAccessorDecl->addMember(synParamDecl); - - // For each paramter, we will create an argument expression - // to represent it in the body of the accessor. - // - auto synArg = m_astBuilder->create<VarExpr>(); - synArg->declRef = makeDeclRef(synParamDecl); - synArg->type = paramType; - synArgs.add(synArg); - } - - // Now synthesize the body of the property accessor. - // The body of the accessor will depend on the class of the accessor - // we are synthesizing (e.g., `get` vs. `set`). - // - Stmt* synBodyStmt = nullptr; - auto propertyRef = m_astBuilder->create<MemberExpr>(); - propertyRef->scope = synAccessorDecl->ownedScope; - auto base = m_astBuilder->create<VarExpr>(); - base->scope = propertyRef->scope; - base->name = getName("inner"); - propertyRef->baseExpression = base; - innerProperty = innerAccessorDeclRef.getParent(); - propertyRef->name = requiredMemberDeclRef.getName(); - auto checkedPropertyRefExpr = CheckExpr(propertyRef); - - if (as<GetterDecl>(requiredAccessorDeclRef)) - { - auto synReturn = m_astBuilder->create<ReturnStmt>(); - synReturn->expression = checkedPropertyRefExpr; - - synBodyStmt = synReturn; - } - else if (as<SetterDecl>(requiredAccessorDeclRef)) - { - auto synAssign = m_astBuilder->create<AssignExpr>(); - synAssign->left = checkedPropertyRefExpr; - synAssign->right = synArgs[0]; - - auto synCheckedAssign = checkAssignWithCheckedOperands(synAssign); - - auto synExprStmt = m_astBuilder->create<ExpressionStmt>(); - synExprStmt->expression = synCheckedAssign; - - synBodyStmt = synExprStmt; - } - else - { - // While there are other kinds of accessors than `get` and `set`, - // those are currently only reserved for the internal use in the core module. - // We will not bother with synthesis for those cases. - // - return false; - } - - addModifier(synAccessorDecl, m_astBuilder->create<ForceInlineAttribute>()); - synAccessorDecl->body = synBodyStmt; - - synPropertyDecl->addMember(synAccessorDecl); - - // Register the synthesized accessor. - // - witnessTable->add( - requiredAccessorDeclRef.getDecl(), - RequirementWitness(makeDeclRef(synAccessorDecl))); - } - - // The type of our synthesized property will be the same as the inner property. - // - auto propertyType = getType(m_astBuilder, as<PropertyDecl>(innerProperty)); - synPropertyDecl->type.type = propertyType; - - // The visibility of synthesized decl should be the same as the inner requirement - if (innerProperty.getDecl()->findModifier<VisibilityModifier>()) - { - auto vis = getDeclVisibility(innerProperty.getDecl()); - addVisibilityModifier(synPropertyDecl, vis); - } - - context->parentDecl->addMember(synPropertyDecl); - witnessTable->add( - requiredMemberDeclRef.getDecl(), - RequirementWitness(makeDeclRef(synPropertyDecl))); - return true; -} - -bool SemanticsVisitor::trySynthesizeAssociatedTypeRequirementWitness( - ConformanceCheckingContext* context, - LookupResult const& inLookupResult, - DeclRef<AssocTypeDecl> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable) -{ - SLANG_UNUSED(inLookupResult); - - // The only case we can synthesize for now is when the conformant type - // is a wrapper type. - if (!isWrapperTypeDecl(context->parentDecl)) - return false; - auto aggTypeDecl = as<AggTypeDecl>(context->parentDecl); - auto lookupResult = lookUpMember( - m_astBuilder, - this, - requiredMemberDeclRef.getName(), - aggTypeDecl->wrappedType.type, - aggTypeDecl->ownedScope, - LookupMask::Default, - LookupOptions::IgnoreBaseInterfaces); - if (!lookupResult.isValid() || lookupResult.isOverloaded()) - return false; - auto assocType = DeclRefType::create(m_astBuilder, lookupResult.item.declRef); - witnessTable->add(requiredMemberDeclRef.getDecl(), assocType); - for (auto typeConstraintDecl : - getMembersOfType<TypeConstraintDecl>(m_astBuilder, requiredMemberDeclRef)) - { - auto witness = tryGetSubtypeWitness(assocType, getSup(m_astBuilder, typeConstraintDecl)); - if (!witness) - return false; - witnessTable->add(typeConstraintDecl.getDecl(), witness); - } - return true; -} - -bool SemanticsVisitor::trySynthesizeAssociatedConstantRequirementWitness( - ConformanceCheckingContext* context, - LookupResult const& inLookupResult, - DeclRef<VarDeclBase> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable) -{ - SLANG_UNUSED(inLookupResult); - - // The only case we can synthesize for now is when the conformant type - // is a wrapper type, i.e. - // struct Foo:IFoo = FooImpl; - if (!isWrapperTypeDecl(context->parentDecl)) - return false; - - // Find the witness that FooImpl : IFoo. - auto aggTypeDecl = as<AggTypeDecl>(context->parentDecl); - auto innerType = aggTypeDecl->wrappedType.type; - DeclRef<Decl> innerProperty; - auto innerWitness = tryGetSubtypeWitness(innerType, witnessTable->baseType); - if (!innerWitness) - return false; - - auto witness = - tryLookUpRequirementWitness(m_astBuilder, innerWitness, requiredMemberDeclRef.getDecl()); - if (witness.getFlavor() != RequirementWitness::Flavor::val) - return false; - witnessTable->add(requiredMemberDeclRef.getDecl(), witness.getVal()); - return true; -} - bool SemanticsVisitor::synthesizeAccessorRequirements( ConformanceCheckingContext* context, DeclRef<ContainerDecl> requiredMemberDeclRef, @@ -6609,92 +6264,12 @@ bool SemanticsVisitor::synthesizeAccessorRequirements( return true; } -bool SemanticsVisitor::trySynthesizeWrapperTypeSubscriptRequirementWitness( - ConformanceCheckingContext* context, - DeclRef<SubscriptDecl> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable) -{ - // We are synthesizing the subscript requirement for a wrapper type: - // struct Wrapper - // { - // Inner inner; - // subscript(int index)->int { get { return inner[index]; } - // set { inner[index] = newValue; } - // } - // } - // - // // Find the witness that FooImpl : IFoo. - auto aggTypeDecl = as<AggTypeDecl>(context->parentDecl); - auto innerType = aggTypeDecl->wrappedType.type; - DeclRef<Decl> innerProperty; - auto innerWitness = tryGetSubtypeWitness(innerType, witnessTable->baseType); - if (!innerWitness) - return false; - // - List<Expr*> synArgs; - ThisExpr* synThis; - auto synSubscriptDecl = synthesizeMethodSignatureForRequirementWitness( - context, - requiredMemberDeclRef, - synArgs, - synThis); - auto declType = getType(m_astBuilder, getDefaultDeclRef(synSubscriptDecl).as<SubscriptDecl>()); - synThis->checked = true; - - // Form a `this[args...]` expression that we will use to coerce from - // in the synthesized subscript accessors. - // - DiagnosticSink tempSink(getSourceManager(), nullptr); - SemanticsVisitor subVisitor(withSink(&tempSink)); - auto base = m_astBuilder->create<VarExpr>(); - base->scope = synThis->scope; - base->name = getName("inner"); - - IndexExpr* indexExpr = m_astBuilder->create<IndexExpr>(); - indexExpr->baseExpression = base; - indexExpr->indexExprs = _Move(synArgs); - auto synBaseStorageExpr = subVisitor.CheckTerm(indexExpr); - - if (tempSink.getErrorCount() != 0) - return false; - - // Our synthesized subscript will have an accessor declaration for - // each accessor of the requirement. - // - bool canSynAccessors = synthesizeAccessorRequirements( - context, - requiredMemberDeclRef, - declType, - synBaseStorageExpr, - synSubscriptDecl, - witnessTable); - if (!canSynAccessors) - return false; - - // The visibility of synthesized decl should be the min of the parent decl and the requirement. - if (requiredMemberDeclRef.getDecl()->findModifier<VisibilityModifier>()) - { - auto requirementVisibility = getDeclVisibility(requiredMemberDeclRef.getDecl()); - auto thisVisibility = getDeclVisibility(context->parentDecl); - auto visibility = Math::Min(thisVisibility, requirementVisibility); - addVisibilityModifier(synSubscriptDecl, visibility); - } - - return true; -} - bool SemanticsVisitor::trySynthesizeSubscriptRequirementWitness( ConformanceCheckingContext* context, const LookupResult& lookupResult, DeclRef<SubscriptDecl> requiredMemberDeclRef, RefPtr<WitnessTable> witnessTable) { - if (isWrapperTypeDecl(context->parentDecl)) - return trySynthesizeWrapperTypeSubscriptRequirementWitness( - context, - requiredMemberDeclRef, - witnessTable); - // The situation here is that the context of an inheritance // declaration didn't provide an exact match for a required // subscript. E.g.: @@ -6926,21 +6501,13 @@ bool SemanticsVisitor::trySynthesizeRequirementWitness( } else { - return trySynthesizeAssociatedTypeRequirementWitness( - context, - lookupResult, - requiredAssocTypeDeclRef, - witnessTable); + return false; } } if (auto requiredConstantDeclRef = requiredMemberDeclRef.as<VarDeclBase>()) { - return trySynthesizeAssociatedConstantRequirementWitness( - context, - lookupResult, - requiredConstantDeclRef, - witnessTable); + return false; } if (auto requiredCtor = requiredMemberDeclRef.as<ConstructorDecl>()) @@ -7543,54 +7110,51 @@ bool SemanticsVisitor::findWitnessForInterfaceRequirement( // lookup results that might be usable, but not as-is. // LookupResult lookupResult; - if (!isWrapperTypeDecl(context->parentDecl)) + lookupResult = lookUpMember( + m_astBuilder, + this, + name, + subType, + nullptr, + LookupMask::Default, + LookupOptions::IgnoreBaseInterfaces); + + if (!lookupResult.isValid()) { - lookupResult = lookUpMember( - m_astBuilder, - this, - name, - subType, - nullptr, - LookupMask::Default, - LookupOptions::IgnoreBaseInterfaces); + // If we failed to look up a member with the name of the + // requirement, it may be possible that we can still synthesis the + // implementation if this is one of the known builtin requirements, + // or if the interface method contains a default impl. + // Otherwise, report diagnostic now. - if (!lookupResult.isValid()) + if (requiredMemberDeclRef.getDecl()->hasModifier<BuiltinRequirementModifier>() || + (requiredMemberDeclRef.as<GenericDecl>() && + getInner(requiredMemberDeclRef.as<GenericDecl>()) + ->hasModifier<BuiltinRequirementModifier>())) + { + } + else if ( + requiredMemberDeclRef.as<SubscriptDecl>() && + (as<ArrayExpressionType>(context->conformingType) || + as<VectorExpressionType>(context->conformingType) || + as<MatrixExpressionType>(context->conformingType))) { - // If we failed to look up a member with the name of the - // requirement, it may be possible that we can still synthesis the - // implementation if this is one of the known builtin requirements, - // or if the interface method contains a default impl. - // Otherwise, report diagnostic now. - - if (requiredMemberDeclRef.getDecl()->hasModifier<BuiltinRequirementModifier>() || - (requiredMemberDeclRef.as<GenericDecl>() && - getInner(requiredMemberDeclRef.as<GenericDecl>()) - ->hasModifier<BuiltinRequirementModifier>())) - { - } - else if ( - requiredMemberDeclRef.as<SubscriptDecl>() && - (as<ArrayExpressionType>(context->conformingType) || - as<VectorExpressionType>(context->conformingType) || - as<MatrixExpressionType>(context->conformingType))) - { - } - else if (hasDefaultImpl(requiredMemberDeclRef)) - { - } - else - { - getSink()->diagnose( - inheritanceDecl, - Diagnostics::typeDoesntImplementInterfaceRequirement, - subType, - requiredMemberDeclRef); - getSink()->diagnose( - requiredMemberDeclRef, - Diagnostics::seeDeclarationOf, - requiredMemberDeclRef); - return false; - } + } + else if (hasDefaultImpl(requiredMemberDeclRef)) + { + } + else + { + getSink()->diagnose( + inheritanceDecl, + Diagnostics::typeDoesntImplementInterfaceRequirement, + subType, + requiredMemberDeclRef); + getSink()->diagnose( + requiredMemberDeclRef, + Diagnostics::seeDeclarationOf, + requiredMemberDeclRef); + return false; } } if (lookupResult.isOverloaded()) @@ -7649,10 +7213,6 @@ bool SemanticsVisitor::findWitnessForInterfaceRequirement( // code required to handle all the conversions that might be // required on `this`. // - // Another situation that will get us here is that we are dealing with - // a wrapper type (struct Foo:IFoo=FooImpl), and we will synthesize - // wrappers that redirects the call into the inner element. - // MethodWitnessSynthesisFailureDetails failureDetails = {}; if (trySynthesizeRequirementWitness( context, @@ -8056,6 +7616,32 @@ bool SemanticsVisitor::checkConformance( // code to work. return true; } + + // If sub type is a link-time-resolved wrapper type (e.g. `extern struct Foo : IFoo = + // FooImpl;`), fill in `inheritanceDecl->witnessVal` with a Witness val that shows `FooImpl + // : IFoo`. + auto aggTypeDecl = as<AggTypeDecl>(declRef.getDecl()); + + if (aggTypeDecl && aggTypeDecl->aliasedType) + { + auto witness = tryGetSubtypeWitness(aggTypeDecl->aliasedType, superType); + if (witness) + { + inheritanceDecl->witnessVal = witness; + } + else + { + if (!as<ErrorType>(aggTypeDecl->aliasedType)) + { + getSink()->diagnose( + inheritanceDecl, + Diagnostics::typeArgumentDoesNotConformToInterface, + aggTypeDecl->aliasedType, + superType); + } + } + return witness != nullptr; + } } // Look at the type being inherited from, and validate diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index 511834cef..b8e68e28b 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -290,6 +290,13 @@ void addSiblingScopeForContainerDecl(ASTBuilder* builder, Scope* destScope, Cont destScope->nextSibling = subScope; } +ContainerDecl* isStaticScopeDecl(Decl* decl) +{ + if (as<NamespaceDeclBase>(decl) || as<FileDecl>(decl)) + return as<ContainerDecl>(decl); + return nullptr; +} + void SemanticsVisitor::diagnoseDeprecatedDeclRefUsage( DeclRef<Decl> declRef, SourceLoc loc, diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index d5d9c2372..599eed12c 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -2009,33 +2009,12 @@ public: DeclRef<PropertyDecl> requiredMemberDeclRef, RefPtr<WitnessTable> witnessTable); - bool trySynthesizeWrapperTypePropertyRequirementWitness( - ConformanceCheckingContext* context, - DeclRef<PropertyDecl> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable); - bool trySynthesizeSubscriptRequirementWitness( ConformanceCheckingContext* context, const LookupResult& lookupResult, DeclRef<SubscriptDecl> requiredMemberDeclRef, RefPtr<WitnessTable> witnessTable); - bool trySynthesizeWrapperTypeSubscriptRequirementWitness( - ConformanceCheckingContext* context, - DeclRef<SubscriptDecl> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable); - - bool trySynthesizeAssociatedTypeRequirementWitness( - ConformanceCheckingContext* context, - LookupResult const& lookupResult, - DeclRef<AssocTypeDecl> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable); - - bool trySynthesizeAssociatedConstantRequirementWitness( - ConformanceCheckingContext* context, - LookupResult const& lookupResult, - DeclRef<VarDeclBase> requiredMemberDeclRef, - RefPtr<WitnessTable> witnessTable); /// Attempt to synthesize a declartion that can satisfy `requiredMemberDeclRef` using /// `lookupResult`. diff --git a/source/slang/slang-ir-insts-stable-names.lua b/source/slang/slang-ir-insts-stable-names.lua index 4b56cc52f..7032f2016 100644 --- a/source/slang/slang-ir-insts-stable-names.lua +++ b/source/slang/slang-ir-insts-stable-names.lua @@ -677,4 +677,5 @@ return { ["Decoration.DisableCopyEliminationDecoration"] = 673, ["Decoration.TempCallArgImmutableVar"] = 674, ["CastResourceToDescriptorHandle"] = 675, + ["SymbolAlias"] = 676, } diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 7b27f0b56..42db9cb44 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -3903,6 +3903,8 @@ public: return (IRMetalMeshType*)getType(kIROp_MetalMeshType, 5, ops); } + IRInst* emitSymbolAlias(IRInst* aliasedSymbol); + IRInst* emitDebugSource( UnownedStringSlice fileName, UnownedStringSlice source, diff --git a/source/slang/slang-ir-insts.lua b/source/slang/slang-ir-insts.lua index 8b8515424..4093db8fa 100644 --- a/source/slang/slang-ir-insts.lua +++ b/source/slang/slang-ir-insts.lua @@ -479,6 +479,12 @@ local insts = { module = { struct_name = "ModuleInst", parent = true }, }, { block = { parent = true } }, + + -- A global inst representing an alias of another symbol, under a different mangled name. + -- This inst should be completely eliminated after linking, with its references replaced + -- to use the canonical symbol being aliased. + { SymbolAlias = { min_operands = 1 } }, + -- IRConstant { Constant = { diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index c46c57043..8ba1f354d 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -302,6 +302,7 @@ IRInst* IRSpecContext::maybeCloneValue(IRInst* originalValue) case kIROp_GlobalGenericParam: case kIROp_WitnessTable: case kIROp_InterfaceType: + case kIROp_SymbolAlias: return cloneGlobalValue(this, originalValue); case kIROp_BoolLit: @@ -346,7 +347,6 @@ IRInst* IRSpecContext::maybeCloneValue(IRInst* originalValue) return builder->getVoidValue(); } break; - default: { // In the default case, assume that we have some sort of "hoistable" @@ -1411,7 +1411,10 @@ IRInst* cloneInst( builder, cast<IRGlobalGenericParam>(originalInst), originalValues); - + case kIROp_SymbolAlias: + // If we encounter a symbol alias, we want to clone + // the value it refers to instead of the alias itself. + return context->maybeCloneValue(cast<IRSymbolAlias>(originalInst)->getOperand(0)); default: break; } diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index d114a9a40..ebaebcc8a 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -3394,6 +3394,11 @@ IRInst* IRBuilder::emitOutImplicitCast(IRInst* type, IRInst* value) { return emitIntrinsicInst((IRType*)type, kIROp_OutImplicitCast, 1, &value); } +IRInst* IRBuilder::emitSymbolAlias(IRInst* aliasedSymbol) +{ + return emitIntrinsicInst(aliasedSymbol->getFullType(), kIROp_SymbolAlias, 1, &aliasedSymbol); +} + IRInst* IRBuilder::emitDebugSource( UnownedStringSlice fileName, UnownedStringSlice source, diff --git a/source/slang/slang-language-server-ast-lookup.cpp b/source/slang/slang-language-server-ast-lookup.cpp index cfb8e4d61..0c3f5f7c1 100644 --- a/source/slang/slang-language-server-ast-lookup.cpp +++ b/source/slang/slang-language-server-ast-lookup.cpp @@ -855,7 +855,7 @@ bool _findAstNodeImpl(ASTLookupContext& context, SyntaxNode* node) if (auto aggTypeDecl = as<AggTypeDecl>(container)) { ASTLookupExprVisitor visitor(&context); - if (visitor.dispatchIfNotNull(aggTypeDecl->wrappedType.exp)) + if (visitor.dispatchIfNotNull(aggTypeDecl->aliasedType.exp)) return true; } } diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 12545ad0d..d3f7d4e4c 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -8593,12 +8593,48 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> LoweredValInfo visitInheritanceDecl(InheritanceDecl* inheritanceDecl) { + // If the inheritance decl is nested inside a link-time type alias declaration, + // e.g. in `export struct Foo:IFoo = FooImpl`, + // then we need to emit a symbo alias to the `FooImpl:IFoo`. + // + auto parentDecl = inheritanceDecl->parentDecl; + auto aggTypeParentDecl = as<AggTypeDecl>(parentDecl); + if (aggTypeParentDecl && aggTypeParentDecl->aliasedType.type && inheritanceDecl->witnessVal) + { + NestedContext nested(this); + auto subBuilder = nested.getBuilder(); + auto subContext = nested.getContext(); + auto outerGeneric = emitOuterGenerics(subContext, inheritanceDecl, inheritanceDecl); + + auto wrappedWitness = lowerVal(subContext, inheritanceDecl->witnessVal); + IRInst* alias = nullptr; + if (outerGeneric) + { + alias = finishOuterGenerics(subBuilder, wrappedWitness.val, outerGeneric); + } + else + { + alias = getBuilder()->emitSymbolAlias(wrappedWitness.val); + } + auto mangledName = getMangledNameForConformanceWitness( + context->astBuilder, + parentDecl, + inheritanceDecl->base.type); + bool explicitExtern = false; + if (isImportedDecl(context, parentDecl, explicitExtern)) + getBuilder()->addImportDecoration(alias, mangledName.getUnownedSlice()); + else + getBuilder()->addExportDecoration(alias, mangledName.getUnownedSlice()); + + context->setGlobalValue(inheritanceDecl, LoweredValInfo::simple(alias)); + return LoweredValInfo::simple(alias); + } + // An inheritance clause inside of an `interface` // declaration should not give rise to a witness // table, because it represents something the // interface requires, and not what it provides. // - auto parentDecl = inheritanceDecl->parentDecl; if (const auto parentInterfaceDecl = as<InterfaceDecl>(parentDecl)) { return LoweredValInfo::simple(getInterfaceRequirementKey(inheritanceDecl)); @@ -9724,10 +9760,6 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> SLANG_UNREACHABLE("associatedtype should have been handled by visitAssocTypeDecl."); } - // TODO(JS): - // Not clear what to do around HLSLExportModifier. - // The HLSL spec says it only applies to functions, so we ignore for now. - // We are going to create nested IR building state // to use when emitting the members of the type. // @@ -9738,6 +9770,35 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // Emit any generics that should wrap the actual type. auto outerGeneric = emitOuterGenerics(subContext, decl, decl); + if (decl->aliasedType) + { + // If the type decl is an alias of another type, then we lower it into + // a IRSymbolAlias. + auto loweredType = lowerType(subContext, decl->aliasedType); + if (loweredType) + { + IRInst* alias = nullptr; + if (outerGeneric) + { + alias = finishOuterGenerics(subBuilder, loweredType, outerGeneric); + } + else + { + alias = subBuilder->emitSymbolAlias(loweredType); + } + addLinkageDecoration(subContext, alias, decl); + + // Enumerate all witnesses and lower IRSymbolAlias for them as well. + for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>()) + { + if (!inheritanceDecl->witnessVal) + continue; + ensureDecl(subContext, inheritanceDecl); + } + return LoweredValInfo::simple(alias); + } + } + IRType* irAggType = nullptr; if (as<StructDecl>(decl)) { diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 605e50d41..3c4c23a42 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -2205,386 +2205,413 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter( varLayout->flags |= VarLayoutFlag::HasSemantic; } - // Scalar and vector types are treated as outputs directly - if (auto basicType = as<BasicExpressionType>(type)) + // We use a lambda here to process the parameter based on `type`. + // We need to be able to recurse on the lambda if need to translate/resolve + // `type` to something else, in that case we simply call the lambda recursively. + auto processParamOfType = [&](auto&& processParamOfTypeFunc, Type* type) -> RefPtr<TypeLayout> { - return processSimpleEntryPointParameter(context, basicType, state, varLayout); - } - else if (auto vectorType = as<VectorExpressionType>(type)) - { - return processSimpleEntryPointParameter(context, vectorType, state, varLayout); - } - // A matrix is processed as if it was an array of rows - else if (auto matrixType = as<MatrixExpressionType>(type)) - { - auto foldedRowCountVal = - context->getTargetProgram()->getProgram()->tryFoldIntVal(matrixType->getRowCount()); - IntegerLiteralValue rowCount = 0; - if (!foldedRowCountVal) + // Scalar and vector types are treated as outputs directly + if (auto basicType = as<BasicExpressionType>(type)) { - rowCount = getIntVal(foldedRowCountVal); + return processSimpleEntryPointParameter(context, basicType, state, varLayout); } - return processSimpleEntryPointParameter( - context, - matrixType, - state, - varLayout, - (int)rowCount); - } - else if (auto arrayType = as<ArrayExpressionType>(type)) - { - // Note: Bad Things will happen if we have an array input - // without a semantic already being enforced. - UInt elementCount = 0; - - if (!arrayType->isUnsized()) + else if (auto vectorType = as<VectorExpressionType>(type)) { - auto intVal = context->getTargetProgram()->getProgram()->tryFoldIntVal( - arrayType->getElementCount()); - if (intVal) - elementCount = (UInt)getIntVal(intVal); + return processSimpleEntryPointParameter(context, vectorType, state, varLayout); } - - // We use the first element to derive the layout for the element type - auto elementTypeLayout = processEntryPointVaryingParameter( - context, - arrayType->getElementType(), - state, - varLayout); - - // We still walk over subsequent elements to make sure they consume resources - // as needed - for (UInt ii = 1; ii < elementCount; ++ii) + // A matrix is processed as if it was an array of rows + else if (auto matrixType = as<MatrixExpressionType>(type)) { - processEntryPointVaryingParameter(context, arrayType->getElementType(), state, nullptr); + auto foldedRowCountVal = + context->getTargetProgram()->getProgram()->tryFoldIntVal(matrixType->getRowCount()); + IntegerLiteralValue rowCount = 0; + if (!foldedRowCountVal) + { + rowCount = getIntVal(foldedRowCountVal); + } + return processSimpleEntryPointParameter( + context, + matrixType, + state, + varLayout, + (int)rowCount); } - - RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); - arrayTypeLayout->elementTypeLayout = elementTypeLayout; - arrayTypeLayout->type = arrayType; - - for (auto rr : elementTypeLayout->resourceInfos) + else if (auto arrayType = as<ArrayExpressionType>(type)) { - arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count * elementCount; - } - - return arrayTypeLayout; - } - else if (auto meshOutputType = as<MeshOutputType>(type)) - { - // TODO: Ellie, revisit - // Note: Bad Things will happen if we have an array input - // without a semantic already being enforced. + // Note: Bad Things will happen if we have an array input + // without a semantic already being enforced. + UInt elementCount = 0; - // We use the first element to derive the layout for the element type - auto elementTypeLayout = processEntryPointVaryingParameter( - context, - meshOutputType->getElementType(), - state, - varLayout); + if (!arrayType->isUnsized()) + { + auto intVal = context->getTargetProgram()->getProgram()->tryFoldIntVal( + arrayType->getElementCount()); + if (intVal) + elementCount = (UInt)getIntVal(intVal); + } - RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); - arrayTypeLayout->elementTypeLayout = elementTypeLayout; - arrayTypeLayout->type = arrayType; + // We use the first element to derive the layout for the element type + auto elementTypeLayout = processEntryPointVaryingParameter( + context, + arrayType->getElementType(), + state, + varLayout); - // TODO: Ellie, this is probably not the right place to handle this - // On GLSL the indices type is built in and as such doesn't consume - // resources. - if (!isKhronosTarget(context->getTargetRequest()) || !as<IndicesType>(type)) - { - for (auto rr : elementTypeLayout->resourceInfos) + // We still walk over subsequent elements to make sure they consume resources + // as needed + for (UInt ii = 1; ii < elementCount; ++ii) { - // TODO: Ellie, explain why only one slot is consumed here - arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count; + processEntryPointVaryingParameter( + context, + arrayType->getElementType(), + state, + nullptr); } - } - return arrayTypeLayout; - } - else if (auto patchType = as<HLSLPatchType>(type)) - { - // Similar to the MeshOutput case, a `InputPatch` or `OutputPatch` type is just like an - // array. - // - auto elementTypeLayout = processEntryPointVaryingParameter( - context, - patchType->getElementType(), - state, - varLayout); + RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); + arrayTypeLayout->elementTypeLayout = elementTypeLayout; + arrayTypeLayout->type = arrayType; - RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); - arrayTypeLayout->elementTypeLayout = elementTypeLayout; - arrayTypeLayout->type = arrayType; + for (auto rr : elementTypeLayout->resourceInfos) + { + arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count * elementCount; + } - for (auto rr : elementTypeLayout->resourceInfos) - { - arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count; + return arrayTypeLayout; } - - return arrayTypeLayout; - } - // Ignore a bunch of types that don't make sense here... - else if (const auto subpassType = as<SubpassInputType>(type)) - { - return nullptr; - } - else if (const auto textureType = as<TextureType>(type)) - { - return nullptr; - } - else if (const auto samplerStateType = as<SamplerStateType>(type)) - { - return nullptr; - } - else if (const auto constantBufferType = as<ConstantBufferType>(type)) - { - return nullptr; - } - else if (auto ptrType = as<PtrType>(type)) - { - SLANG_ASSERT(ptrType->astNodeType == ASTNodeType::PtrType); - - auto typeLayout = processSimpleEntryPointParameter(context, ptrType, state, varLayout); - RefPtr<PointerTypeLayout> ptrTypeLayout = typeLayout.as<PointerTypeLayout>(); - - // Work out the layout for the value/target type - auto valueTypeLayout = - processEntryPointVaryingParameter(context, ptrType->getValueType(), state, varLayout); - ptrTypeLayout->valueTypeLayout = valueTypeLayout; - return ptrTypeLayout; - } - else if (auto optionalType = as<OptionalType>(type)) - { - Array<Type*, 2> types = - makeArray(optionalType->getValueType(), context->getASTBuilder()->getBoolType()); - auto tupleType = context->getASTBuilder()->getTupleType(types.getView()); - return processEntryPointVaryingParameter(context, tupleType, state, varLayout); - } - else if (auto tupleType = as<TupleType>(type)) - { - RefPtr<StructTypeLayout> structLayout = new StructTypeLayout(); - structLayout->type = type; - for (Index i = 0; i < tupleType->getMemberCount(); i++) + else if (auto meshOutputType = as<MeshOutputType>(type)) { - auto fieldType = tupleType->getMember(i); - RefPtr<VarLayout> fieldVarLayout = new VarLayout(); - - // We don't really have a "field" decl, so just use the tuple-typed decl - // itself as the varDecl of the elements. - auto fieldDecl = (VarDeclBase*)varLayout->varDecl.getDecl(); - fieldVarLayout->varDecl = fieldDecl; + // TODO: Ellie, revisit + // Note: Bad Things will happen if we have an array input + // without a semantic already being enforced. - structLayout->fields.add(fieldVarLayout); - - auto fieldTypeLayout = processEntryPointVaryingParameterDecl( + // We use the first element to derive the layout for the element type + auto elementTypeLayout = processEntryPointVaryingParameter( context, - fieldDecl, - fieldType, + meshOutputType->getElementType(), state, - fieldVarLayout); + varLayout); + + RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); + arrayTypeLayout->elementTypeLayout = elementTypeLayout; + arrayTypeLayout->type = arrayType; - if (!fieldTypeLayout) + // TODO: Ellie, this is probably not the right place to handle this + // On GLSL the indices type is built in and as such doesn't consume + // resources. + if (!isKhronosTarget(context->getTargetRequest()) || !as<IndicesType>(type)) { - getSink(context)->diagnose( - varLayout->varDecl, - Diagnostics::notValidVaryingParameter, - fieldType); - continue; + for (auto rr : elementTypeLayout->resourceInfos) + { + // TODO: Ellie, explain why only one slot is consumed here + arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count; + } } - fieldVarLayout->typeLayout = fieldTypeLayout; - // Assign offsets in var layout for each resource kind of the type. - for (auto fieldTypeResInfo : fieldTypeLayout->resourceInfos) + return arrayTypeLayout; + } + else if (auto patchType = as<HLSLPatchType>(type)) + { + // Similar to the MeshOutput case, a `InputPatch` or `OutputPatch` type is just like an + // array. + // + auto elementTypeLayout = processEntryPointVaryingParameter( + context, + patchType->getElementType(), + state, + varLayout); + + RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); + arrayTypeLayout->elementTypeLayout = elementTypeLayout; + arrayTypeLayout->type = arrayType; + + for (auto rr : elementTypeLayout->resourceInfos) { - auto kind = fieldTypeResInfo.kind; - auto structTypeResInfo = structLayout->findOrAddResourceInfo(kind); - auto fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind); - fieldResInfo->index = structTypeResInfo->count.getFiniteValue(); - structTypeResInfo->count += fieldTypeResInfo.count; + arrayTypeLayout->findOrAddResourceInfo(rr.kind)->count = rr.count; } - } - return structLayout; - } - // Catch declaration-reference types late in the sequence, since - // otherwise they will include all of the above cases... - else if (auto declRefType = as<DeclRefType>(type)) - { - // If we are trying to get the layout of some extern type, do our best - // to look it up in other loaded modules and generate the type layout - // based on that. - declRefType = context->layoutContext.lookupExternDeclRefType(declRefType); - auto declRef = declRefType->getDeclRef(); + return arrayTypeLayout; + } + // Ignore a bunch of types that don't make sense here... + else if (const auto subpassType = as<SubpassInputType>(type)) + { + return nullptr; + } + else if (const auto textureType = as<TextureType>(type)) + { + return nullptr; + } + else if (const auto samplerStateType = as<SamplerStateType>(type)) + { + return nullptr; + } + else if (const auto constantBufferType = as<ConstantBufferType>(type)) + { + return nullptr; + } + else if (auto ptrType = as<PtrType>(type)) + { + SLANG_ASSERT(ptrType->astNodeType == ASTNodeType::PtrType); + auto typeLayout = processSimpleEntryPointParameter(context, ptrType, state, varLayout); + RefPtr<PointerTypeLayout> ptrTypeLayout = typeLayout.as<PointerTypeLayout>(); - if (auto structDeclRef = declRef.as<StructDecl>()) + // Work out the layout for the value/target type + auto valueTypeLayout = processEntryPointVaryingParameter( + context, + ptrType->getValueType(), + state, + varLayout); + ptrTypeLayout->valueTypeLayout = valueTypeLayout; + return ptrTypeLayout; + } + else if (auto optionalType = as<OptionalType>(type)) + { + Array<Type*, 2> types = + makeArray(optionalType->getValueType(), context->getASTBuilder()->getBoolType()); + auto tupleType = context->getASTBuilder()->getTupleType(types.getView()); + return processEntryPointVaryingParameter(context, tupleType, state, varLayout); + } + else if (auto tupleType = as<TupleType>(type)) { RefPtr<StructTypeLayout> structLayout = new StructTypeLayout(); - structLayout->type = declRefType; - - // We will recursively walk the fields of a `struct` type - // to compute layouts for those fields. - // - // Along the way, we may find fields with explicit layout - // annotations, along with fields that have no explicit - // layout. We will consider it an error to have a mix of - // the two. - // - // TODO: We could support a mix of implicit and explicit - // layout by performing layout on fields in two passes, - // much like is done for the global scope. This would - // complicate layout significantly for little practical - // benefit, so it is very much a "nice to have" rather - // than a "must have" feature. - // - Decl* firstExplicit = nullptr; - Decl* firstImplicit = nullptr; - for (auto field : - getFields(context->getASTBuilder(), structDeclRef, MemberFilterStyle::Instance)) + structLayout->type = type; + for (Index i = 0; i < tupleType->getMemberCount(); i++) { + auto fieldType = tupleType->getMember(i); RefPtr<VarLayout> fieldVarLayout = new VarLayout(); - fieldVarLayout->varDecl = field; + + // We don't really have a "field" decl, so just use the tuple-typed decl + // itself as the varDecl of the elements. + auto fieldDecl = (VarDeclBase*)varLayout->varDecl.getDecl(); + fieldVarLayout->varDecl = fieldDecl; structLayout->fields.add(fieldVarLayout); - structLayout->mapVarToLayout.add(field.getDecl(), fieldVarLayout); auto fieldTypeLayout = processEntryPointVaryingParameterDecl( context, - field.getDecl(), - getType(context->getASTBuilder(), field), + fieldDecl, + fieldType, state, fieldVarLayout); if (!fieldTypeLayout) { - getSink(context)->diagnose(field, Diagnostics::notValidVaryingParameter, field); + getSink(context)->diagnose( + varLayout->varDecl, + Diagnostics::notValidVaryingParameter, + fieldType); continue; } fieldVarLayout->typeLayout = fieldTypeLayout; - // The field needs to have offset information stored - // in `fieldVarLayout` for every kind of resource - // consumed by `fieldTypeLayout`. - // + // Assign offsets in var layout for each resource kind of the type. for (auto fieldTypeResInfo : fieldTypeLayout->resourceInfos) { - // If the field is a Conditional<T, false> type, then it could have 0 size. - // We should skip this field if it has no use of layout units. - if (fieldTypeResInfo.count == 0) - continue; - auto kind = fieldTypeResInfo.kind; - auto structTypeResInfo = structLayout->findOrAddResourceInfo(kind); + auto fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind); + fieldResInfo->index = structTypeResInfo->count.getFiniteValue(); + structTypeResInfo->count += fieldTypeResInfo.count; + } + } + return structLayout; + } + // Catch declaration-reference types late in the sequence, since + // otherwise they will include all of the above cases... + else if (auto declRefType = as<DeclRefType>(type)) + { + // If we are trying to get the layout of some extern type, do our best + // to look it up in other loaded modules and generate the type layout + // based on that. + auto lookedUpType = context->layoutContext.lookupExternDeclRefType(declRefType); + + // If the link-time type resolved to something concrete, process the param as if it is + // of the concrete type by recursively calling this lambda. + if (type != lookedUpType) + return processParamOfTypeFunc(_Move(processParamOfTypeFunc), lookedUpType); + + auto declRef = declRefType->getDeclRef(); + + if (auto structDeclRef = declRef.as<StructDecl>()) + { + RefPtr<StructTypeLayout> structLayout = new StructTypeLayout(); + structLayout->type = declRefType; + + // We will recursively walk the fields of a `struct` type + // to compute layouts for those fields. + // + // Along the way, we may find fields with explicit layout + // annotations, along with fields that have no explicit + // layout. We will consider it an error to have a mix of + // the two. + // + // TODO: We could support a mix of implicit and explicit + // layout by performing layout on fields in two passes, + // much like is done for the global scope. This would + // complicate layout significantly for little practical + // benefit, so it is very much a "nice to have" rather + // than a "must have" feature. + // + Decl* firstExplicit = nullptr; + Decl* firstImplicit = nullptr; + for (auto field : getFields( + context->getASTBuilder(), + structDeclRef, + MemberFilterStyle::Instance)) + { + RefPtr<VarLayout> fieldVarLayout = new VarLayout(); + fieldVarLayout->varDecl = field; + + structLayout->fields.add(fieldVarLayout); + structLayout->mapVarToLayout.add(field.getDecl(), fieldVarLayout); + + auto fieldTypeLayout = processEntryPointVaryingParameterDecl( + context, + field.getDecl(), + getType(context->getASTBuilder(), field), + state, + fieldVarLayout); - auto fieldResInfo = fieldVarLayout->FindResourceInfo(kind); - if (!fieldResInfo) + if (!fieldTypeLayout) { - if (!firstImplicit) - firstImplicit = field.getDecl(); - - // In the implicit-layout case, we assign the field - // the next available offset after the fields that - // have preceded it. - // - fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind); - fieldResInfo->index = structTypeResInfo->count.getFiniteValue(); - structTypeResInfo->count += fieldTypeResInfo.count; + getSink(context)->diagnose( + field, + Diagnostics::notValidVaryingParameter, + field); + continue; } - else + fieldVarLayout->typeLayout = fieldTypeLayout; + + // The field needs to have offset information stored + // in `fieldVarLayout` for every kind of resource + // consumed by `fieldTypeLayout`. + // + for (auto fieldTypeResInfo : fieldTypeLayout->resourceInfos) { - if (!firstExplicit) - firstExplicit = field.getDecl(); - - // In the explicit case, the field already has offset - // information, and we just need to update the computed - // size of the `struct` type to account for the field. - // - auto fieldEndOffset = fieldResInfo->index + fieldTypeResInfo.count; - structTypeResInfo->count = - maximum(structTypeResInfo->count, fieldEndOffset); + // If the field is a Conditional<T, false> type, then it could have 0 size. + // We should skip this field if it has no use of layout units. + if (fieldTypeResInfo.count == 0) + continue; + + auto kind = fieldTypeResInfo.kind; + + auto structTypeResInfo = structLayout->findOrAddResourceInfo(kind); + + auto fieldResInfo = fieldVarLayout->FindResourceInfo(kind); + if (!fieldResInfo) + { + if (!firstImplicit) + firstImplicit = field.getDecl(); + + // In the implicit-layout case, we assign the field + // the next available offset after the fields that + // have preceded it. + // + fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind); + fieldResInfo->index = structTypeResInfo->count.getFiniteValue(); + structTypeResInfo->count += fieldTypeResInfo.count; + } + else + { + if (!firstExplicit) + firstExplicit = field.getDecl(); + + // In the explicit case, the field already has offset + // information, and we just need to update the computed + // size of the `struct` type to account for the field. + // + auto fieldEndOffset = fieldResInfo->index + fieldTypeResInfo.count; + structTypeResInfo->count = + maximum(structTypeResInfo->count, fieldEndOffset); + } } } + if (firstImplicit && firstExplicit) + { + getSink(context)->diagnose( + firstImplicit, + Diagnostics::mixingImplicitAndExplicitBindingForVaryingParams, + firstImplicit->getName(), + firstExplicit->getName()); + } + + return structLayout; } - if (firstImplicit && firstExplicit) + else if (auto globalGenericParamDecl = declRef.as<GlobalGenericParamDecl>()) { - getSink(context)->diagnose( - firstImplicit, - Diagnostics::mixingImplicitAndExplicitBindingForVaryingParams, - firstImplicit->getName(), - firstExplicit->getName()); - } - - return structLayout; - } - else if (auto globalGenericParamDecl = declRef.as<GlobalGenericParamDecl>()) - { - auto& layoutContext = context->layoutContext; + auto& layoutContext = context->layoutContext; - if (auto concreteType = findGlobalGenericSpecializationArg( - layoutContext, - globalGenericParamDecl.getDecl())) + if (auto concreteType = findGlobalGenericSpecializationArg( + layoutContext, + globalGenericParamDecl.getDecl())) + { + // If we know what concrete type has been used to specialize + // the global generic type parameter, then we should use + // the concrete type instead. + // + // Note: it should be illegal for the user to use a generic + // type parameter in a varying parameter list without giving + // it an explicit user-defined semantic. Otherwise, it would be possible + // that the concrete type that gets plugged in is a user-defined + // `struct` that uses some `SV_` semantics in its definition, + // so that any static information about what system values + // the entry point uses would be incorrect. + // + return processEntryPointVaryingParameter( + context, + concreteType, + state, + varLayout); + } + else + { + // If we don't know a concrete type, then we aren't generating final + // code, so the reflection information should show the generic + // type parameter. + // + // We don't make any attempt to assign varying parameter resources + // to the generic type, since we can't know how many "slots" + // of varying input/output it would consume. + // + return createTypeLayoutForGlobalGenericTypeParam( + layoutContext, + type, + globalGenericParamDecl.getDecl()); + } + } + else if (auto enumDeclRef = declRef.as<EnumDecl>()) { - // If we know what concrete type has been used to specialize - // the global generic type parameter, then we should use - // the concrete type instead. + // We handle an enumeration type as its tag type for varying parameters. + // This allows enums to be used in vertex output/input similar to their + // underlying integer types. // - // Note: it should be illegal for the user to use a generic - // type parameter in a varying parameter list without giving - // it an explicit user-defined semantic. Otherwise, it would be possible - // that the concrete type that gets plugged in is a user-defined - // `struct` that uses some `SV_` semantics in its definition, - // so that any static information about what system values - // the entry point uses would be incorrect. - // - return processEntryPointVaryingParameter(context, concreteType, state, varLayout); + auto tagType = enumDeclRef.getDecl()->tagType; + SLANG_ASSERT(tagType); + return processEntryPointVaryingParameter(context, tagType, state, varLayout); + } + else if (auto associatedTypeParam = declRef.as<AssocTypeDecl>()) + { + RefPtr<TypeLayout> assocTypeLayout = new TypeLayout(); + assocTypeLayout->type = type; + return assocTypeLayout; } else { - // If we don't know a concrete type, then we aren't generating final - // code, so the reflection information should show the generic - // type parameter. - // - // We don't make any attempt to assign varying parameter resources - // to the generic type, since we can't know how many "slots" - // of varying input/output it would consume. - // - return createTypeLayoutForGlobalGenericTypeParam( - layoutContext, - type, - globalGenericParamDecl.getDecl()); + SLANG_UNEXPECTED("unhandled type kind"); } } - else if (auto enumDeclRef = declRef.as<EnumDecl>()) - { - // We handle an enumeration type as its tag type for varying parameters. - // This allows enums to be used in vertex output/input similar to their - // underlying integer types. - // - auto tagType = enumDeclRef.getDecl()->tagType; - SLANG_ASSERT(tagType); - return processEntryPointVaryingParameter(context, tagType, state, varLayout); - } - else if (auto associatedTypeParam = declRef.as<AssocTypeDecl>()) - { - RefPtr<TypeLayout> assocTypeLayout = new TypeLayout(); - assocTypeLayout->type = type; - return assocTypeLayout; - } - else + + // If we ran into an error in checking the user's code, then skip this parameter + else if (const auto errorType = as<ErrorType>(type)) { - SLANG_UNEXPECTED("unhandled type kind"); + return nullptr; } - } - // If we ran into an error in checking the user's code, then skip this parameter - else if (const auto errorType = as<ErrorType>(type)) - { - return nullptr; - } - - SLANG_UNEXPECTED("unhandled type kind"); - UNREACHABLE_RETURN(nullptr); + SLANG_UNEXPECTED("unhandled type kind"); + UNREACHABLE_RETURN(nullptr); + }; + return processParamOfType(_Move(processParamOfType), type); } /// Compute the type layout for a parameter declared directly on an entry point. @@ -2606,8 +2633,8 @@ static RefPtr<TypeLayout> computeEntryPointParameterTypeLayout( LayoutRulesImpl* layoutRules = nullptr; if (isKhronosTarget(context->getTargetRequest())) { - // For Vulkan, entry point uniform parameters are laid out using push constant buffer - // rules (defaults to std430). + // For Vulkan, entry point uniform parameters are laid out using push constant + // buffer rules (defaults to std430). layoutRules = context->getRulesFamily()->getShaderStorageBufferRules( context->getTargetProgram()->getOptionSet()); } @@ -3559,15 +3586,16 @@ static void collectParameters(ParameterBindingContext* inContext, ComponentType* /// Emit a diagnostic about a uniform/ordinary parameter at global scope. void diagnoseGlobalUniform(SharedParameterBindingContext* sharedContext, VarDeclBase* varDecl) { - // Don't emit the implicit global shader parameter warning if the variable is explicitly marked - // as uniform + // Don't emit the implicit global shader parameter warning if the variable is explicitly + // marked as uniform if (!varDecl->hasModifier<HLSLUniformModifier>()) { getSink(sharedContext) ->diagnose(varDecl, Diagnostics::globalUniformNotExpected, varDecl->getName()); } - // Always check and warn about binding attributes being ignored, regardless of uniform modifier + // Always check and warn about binding attributes being ignored, regardless of uniform + // modifier if (varDecl->findModifier<GLSLBindingAttribute>()) { sharedContext->m_sink->diagnose( @@ -3635,7 +3663,8 @@ struct ParameterBindingVisitorCounters Index globalParamCounter = 0; }; -/// Recursive routine to "complete" all binding for parameters and entry points in `componentType`. +/// Recursive routine to "complete" all binding for parameters and entry points in +/// `componentType`. /// /// This includes allocation of as-yet-unused register/binding ranges to parameters (which /// will then affect the ranges of registers/bindings that are available to subsequent @@ -3973,8 +4002,8 @@ static bool _calcNeedsDefaultSpace(SharedParameterBindingContext& sharedContext) continue; case LayoutResourceKind::Uniform: { - // If it's uniform, but we have globals binding defined, we don't need a default - // space for it as it will go in the global binding specified + // If it's uniform, but we have globals binding defined, we don't need a + // default space for it as it will go in the global binding specified if (auto hlslToVulkanOptions = sharedContext.getTargetProgram()->getHLSLToVulkanLayoutOptions()) { @@ -4070,8 +4099,8 @@ static void _maybeApplyHLSLToVulkanShifts( return; } - // If the user specified -fvk-b-shift for the default space but not -fvk-bind-global, we want to - // apply the shift to the global constant buffer. + // If the user specified -fvk-b-shift for the default space but not -fvk-bind-global, we + // want to apply the shift to the global constant buffer. if (!vulkanOptions->hasGlobalsBinding()) { auto globalCBufferShift = vulkanOptions->getShift( @@ -4117,10 +4146,10 @@ static void _maybeApplyHLSLToVulkanShifts( // In essence we need to look for HLSL kinds which have inferance. // We assume all map to Descriptor, and look for descriptor overlaps - // We know there can't be a clash of HLSL layout kinds previously, otherwise that - // would have already produced an a warning. We also know the only change is either - // *all* of a set is shifted or none. That means post a shift there still can't be - // clash between HLSL types. + // We know there can't be a clash of HLSL layout kinds previously, otherwise + // that would have already produced an a warning. We also know the only change + // is either *all* of a set is shifted or none. That means post a shift there + // still can't be clash between HLSL types. // So clashes can only be between HLSL types and other bindings (regardless) diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index dde783ce8..3519b6d43 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -5509,7 +5509,7 @@ Decl* Parser::ParseStruct() parseOptionalInheritanceClause(this, rs); if (AdvanceIf(this, TokenType::OpAssign)) { - rs->wrappedType = ParseTypeExp(); + rs->aliasedType = ParseTypeExp(); PushScope(rs); PopScope(); if (!LookAheadToken(TokenType::Semicolon)) diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h index 640fa8fcd..50a319ea6 100644 --- a/source/slang/slang-syntax.h +++ b/source/slang/slang-syntax.h @@ -196,6 +196,11 @@ inline Type* getTargetType(ASTBuilder* astBuilder, DeclRef<ExtensionDecl> declRe return declRef.substitute(astBuilder, declRef.getDecl()->targetType.Ptr()); } +inline Type* getAliasedType(ASTBuilder* astBuilder, DeclRef<AggTypeDecl> declRef) +{ + return declRef.substitute(astBuilder, declRef.getDecl()->aliasedType.Ptr()); +} + inline FilteredMemberRefList<VarDecl> getFields( ASTBuilder* astBuilder, DeclRef<StructDecl> declRef, diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 0de6348bf..5bbcd2eb1 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -5191,9 +5191,11 @@ static TypeLayoutResult _createTypeLayout(TypeLayoutContext& context, Type* type // If we are trying to get the layout of some extern type, do our best // to look it up in other loaded modules and generate the type layout // based on that. - declRefType = context.lookupExternDeclRefType(declRefType); - auto declRef = declRefType->getDeclRef(); + auto resolvedType = context.lookupExternDeclRefType(declRefType); + if (resolvedType != type) + return _createTypeLayout(context, resolvedType); + auto declRef = declRefType->getDeclRef(); if (auto structDeclRef = declRef.as<StructDecl>()) { @@ -5888,26 +5890,71 @@ GlobalGenericParamDecl* GenericParamTypeLayout::getGlobalGenericParamDecl() return rsDeclRef.getDecl(); } -DeclRefType* TypeLayoutContext::lookupExternDeclRefType(DeclRefType* declRefType) +// Get the decl ref to the outer generic if the decl referenced by `declRef` is generic. +DeclRef<GenericDecl> getOuterGeneric(DeclRef<Decl> declRef) +{ + if (auto directDeclRef = as<DirectDeclRef>(declRef.declRefBase)) + { + if (as<GenericDecl>(directDeclRef->getDecl())) + return DeclRef<GenericDecl>(directDeclRef); + if (as<GenericDecl>(directDeclRef->getParent()->getDecl())) + return DeclRef<GenericDecl>(directDeclRef->getParent()); + } + else if (auto genAppDeclRef = as<GenericAppDeclRef>(declRef.declRefBase)) + { + return DeclRef<GenericDecl>(genAppDeclRef->getBase()); + } + return DeclRef<GenericDecl>(); +} + +Type* TypeLayoutContext::lookupExternDeclRefType(DeclRefType* declRefType) { const auto declRef = declRefType->getDeclRef(); const auto decl = declRef.getDecl(); const auto isExtern = decl->hasModifier<ExternAttribute>() || decl->hasModifier<ExternModifier>(); + Type* resultType = declRefType; if (isExtern) { if (!externTypeMap) buildExternTypeMap(); const auto mangledName = getMangledName(targetReq->getLinkage()->getASTBuilder(), decl); - externTypeMap->tryGetValue(mangledName, declRefType); + externTypeMap->tryGetValue(mangledName, resultType); + if (auto resolvedDeclRef = isDeclRefTypeOf<Decl>(resultType)) + { + if (resolvedDeclRef != declRef) + { + // If declRef is a GenericApp, we should replace the generic base to + // resolveDeclRef's base. + if (auto originalGenericApp = as<GenericAppDeclRef>(declRef.declRefBase)) + { + if (auto resolvedOuterGeneric = getOuterGeneric(resolvedDeclRef.getDecl())) + { + auto substGenericApp = astBuilder->getGenericAppDeclRef( + resolvedOuterGeneric, + originalGenericApp->getArgs()); + resultType = DeclRefType::create(astBuilder, substGenericApp); + } + } + } + } + } + + // If the type is an alias of another type, then we should create the type layout + // from the aliased type instead. + if (auto aggTypeDeclRef = isDeclRefTypeOf<AggTypeDecl>(resultType)) + { + if (auto aliasedType = as<Type>(getAliasedType(astBuilder, aggTypeDeclRef))) + { + return aliasedType; + } } - return declRefType; + return resultType; } void TypeLayoutContext::buildExternTypeMap() { externTypeMap.emplace(); - const auto linkage = targetReq->getLinkage(); HashSet<String> externNames; Dictionary<String, DeclRefType*> allTypes; @@ -5916,6 +5963,8 @@ void TypeLayoutContext::buildExternTypeMap() // We'll match them up later auto processDecl = [&](auto&& go, Decl* decl) -> void { + if (auto genericDecl = as<GenericDecl>(decl)) + decl = genericDecl->inner; const auto isExtern = decl->hasModifier<ExternAttribute>() || decl->hasModifier<ExternModifier>(); @@ -5933,7 +5982,7 @@ void TypeLayoutContext::buildExternTypeMap() } } - if (auto scopeDecl = as<ScopeDecl>(decl)) + if (auto scopeDecl = isStaticScopeDecl(decl)) { for (auto member : scopeDecl->getDirectMemberDecls()) { @@ -5942,7 +5991,7 @@ void TypeLayoutContext::buildExternTypeMap() } }; - for (const auto& m : linkage->loadedModulesList) + for (const auto& m : programLayout->getProgram()->getModuleDependencies()) { const auto& ast = m->getModuleDecl(); for (auto member : ast->getDirectMemberDecls()) diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index 84840c043..d47981904 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -1183,11 +1183,11 @@ struct TypeLayoutContext // Options passed to object layout ObjectLayoutRulesImpl::Options objectLayoutOptions; - // Mangled names to DeclRefType, this is used to match up 'extern' types to + // Mangled names to Type, this is used to match up 'extern' types to // their linked in definitions during layout generation - std::optional<Dictionary<String, DeclRefType*>> externTypeMap; + std::optional<Dictionary<String, Type*>> externTypeMap; - DeclRefType* lookupExternDeclRefType(DeclRefType* declRefType); + Type* lookupExternDeclRefType(DeclRefType* declRefType); void buildExternTypeMap(); LayoutRulesImpl* getRules() { return rules; } |
