summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-ast-decl.h23
-rw-r--r--source/slang/slang-ast-iterator.h2
-rw-r--r--source/slang/slang-ast-support-types.h1
-rw-r--r--source/slang/slang-check-decl.cpp586
-rw-r--r--source/slang/slang-check-expr.cpp7
-rw-r--r--source/slang/slang-check-impl.h21
-rw-r--r--source/slang/slang-ir-insts-stable-names.lua1
-rw-r--r--source/slang/slang-ir-insts.h2
-rw-r--r--source/slang/slang-ir-insts.lua6
-rw-r--r--source/slang/slang-ir-link.cpp7
-rw-r--r--source/slang/slang-ir.cpp5
-rw-r--r--source/slang/slang-language-server-ast-lookup.cpp2
-rw-r--r--source/slang/slang-lower-to-ir.cpp71
-rw-r--r--source/slang/slang-parameter-binding.cpp675
-rw-r--r--source/slang/slang-parser.cpp2
-rw-r--r--source/slang/slang-syntax.h5
-rw-r--r--source/slang/slang-type-layout.cpp65
-rw-r--r--source/slang/slang-type-layout.h6
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; }