summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-check-decl.cpp
diff options
context:
space:
mode:
authorkaizhangNV <149626564+kaizhangNV@users.noreply.github.com>2025-02-05 12:37:03 -0600
committerGitHub <noreply@github.com>2025-02-05 10:37:03 -0800
commit9ec6b91686b651d959fd9ffbec283845bd725dd6 (patch)
tree2c48202cb04b76e5ddcb274be35529378ddf8f31 /source/slang/slang-check-decl.cpp
parent4b350645042b8e8fbdad19784ee745d11c7bc616 (diff)
Feature/initialize list side branch (#6058)
* SP004: implement initialize list translation to ctor - We synthesize a member-wise constructor for each struct follow the rules described in SP004. - Add logic to translate the initialize list to constructor invoke - Add cuda-host decoration for the synthesized constructor - Remove the default constructor when we have a valid member init constructor - Disable -zero-initialize option, will re-implement it in followup (#6109). - Fix the overload lookup issue When creating invoke expression for ctor, we need to call ResolveInvoke() to find us the best candidates, however the existing lookup logic could find us the base constructor for child struct, we should eliminate this case by providing the LookupOptions::IgnoreInheritance to lookup, this requires us to create a subcontext on SemanticsVisitor to indicate that we only want to use this option on looking the constructor. - Do not implicit initialize a struct that doesn't have explicit default constructor. Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
-rw-r--r--source/slang/slang-check-decl.cpp540
1 files changed, 466 insertions, 74 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 7d083e53b..b03126512 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -28,6 +28,7 @@ static List<ConstructorDecl*> _getCtorList(
SemanticsVisitor* visitor,
StructDecl* structDecl,
ConstructorDecl** defaultCtorOut);
+static Expr* constructDefaultInitExprForType(SemanticsVisitor* visitor, VarDeclBase* varDecl);
/// Visitor to transition declarations to `DeclCheckState::CheckedModifiers`
struct SemanticsDeclModifiersVisitor : public SemanticsDeclVisitorBase,
@@ -94,6 +95,18 @@ struct SemanticsDeclAttributesVisitor : public SemanticsDeclVisitorBase,
void checkVarDeclCommon(VarDeclBase* varDecl);
void visitVarDecl(VarDecl* varDecl) { checkVarDeclCommon(varDecl); }
+
+ // Synthesize the constructor declaration for a struct during header visit, as we
+ // need to have such declaration first such that the overloading resolution can lookup
+ // such constructor and complete the initialize list to constructor translation.
+ //
+ // We will defer the actual implementation of the constructor to the body visit, because
+ // we will have full information about each field in the struct during that stage.
+ bool _synthesizeCtorSignature(StructDecl* structDecl);
+ bool collectInitializableMembers(
+ StructDecl* structDecl,
+ const DeclVisibility ctorVisibility,
+ List<VarDeclBase*>& resultMembers);
};
struct SemanticsDeclHeaderVisitor : public SemanticsDeclVisitorBase,
@@ -319,6 +332,7 @@ struct SemanticsDeclBodyVisitor : public SemanticsDeclVisitorBase,
SemanticsContext registerDifferentiableTypesForFunc(FunctionDeclBase* funcDecl);
+private:
struct DeclAndCtorInfo
{
StructDecl* parent = nullptr;
@@ -350,13 +364,21 @@ struct SemanticsDeclBodyVisitor : public SemanticsDeclVisitorBase,
ConstructorDecl* ctor,
List<DeclAndCtorInfo>& inheritanceDefaultCtorList,
ThisExpr* thisExpr,
- SeqStmt* seqStmtChild);
+ SeqStmt* seqStmtChild,
+ bool isMemberInitCtor,
+ Index& paramIndex);
+
void synthesizeCtorBodyForMember(
ConstructorDecl* ctor,
Decl* member,
ThisExpr* thisExpr,
Dictionary<Decl*, Expr*>& cachedDeclToCheckedVar,
- SeqStmt* seqStmtChild);
+ SeqStmt* seqStmtChild,
+ bool isMemberInitCtor,
+ Index& paramIndex);
+
+ MemberExpr* createMemberExpr(ThisExpr* thisExpr, Scope* scope, Decl* member);
+ Expr* createCtorParamExpr(ConstructorDecl* ctor, Index paramIndex);
};
template<typename VisitorType>
@@ -2025,45 +2047,80 @@ void SemanticsDeclHeaderVisitor::checkVarDeclCommon(VarDeclBase* varDecl)
checkVisibility(varDecl);
}
-static ConstructorDecl* _createCtor(
+static void addAutoDiffModifiersToFunc(
SemanticsDeclVisitorBase* visitor,
ASTBuilder* m_astBuilder,
- AggTypeDecl* decl)
+ FunctionDeclBase* func)
+{
+ if (visitor->isTypeDifferentiable(func->returnType.type))
+ {
+ addModifier(func, m_astBuilder->create<BackwardDifferentiableAttribute>());
+ addModifier(func, m_astBuilder->create<ForwardDifferentiableAttribute>());
+ }
+ else
+ addModifier(func, m_astBuilder->create<TreatAsDifferentiableAttribute>());
+}
+
+ConstructorDecl* SemanticsDeclVisitorBase::createCtor(
+ AggTypeDecl* decl,
+ DeclVisibility ctorVisibility)
{
auto ctor = m_astBuilder->create<ConstructorDecl>();
addModifier(ctor, m_astBuilder->create<SynthesizedModifier>());
- auto ctorName = visitor->getName("$init");
+ auto ctorName = getName("$init");
ctor->ownedScope = m_astBuilder->create<Scope>();
ctor->ownedScope->containerDecl = ctor;
- ctor->ownedScope->parent = visitor->getScope(decl);
+ ctor->ownedScope->parent = getScope(decl);
ctor->parentDecl = decl;
ctor->loc = decl->loc;
ctor->closingSourceLoc = ctor->loc;
ctor->nameAndLoc.name = ctorName;
ctor->nameAndLoc.loc = ctor->loc;
- ctor->returnType.type = visitor->calcThisType(makeDeclRef(decl));
+ ctor->returnType.type = calcThisType(makeDeclRef(decl));
auto body = m_astBuilder->create<BlockStmt>();
body->scopeDecl = m_astBuilder->create<ScopeDecl>();
body->scopeDecl->ownedScope = m_astBuilder->create<Scope>();
- body->scopeDecl->ownedScope->parent = visitor->getScope(ctor);
+ body->scopeDecl->ownedScope->parent = getScope(ctor);
body->scopeDecl->parentDecl = ctor;
body->scopeDecl->loc = ctor->loc;
body->scopeDecl->closingSourceLoc = ctor->loc;
body->closingSourceLoc = ctor->closingSourceLoc;
ctor->body = body;
body->body = m_astBuilder->create<SeqStmt>();
- ctor->isSynthesized = true;
+ ctor->addFlavor(ConstructorDecl::ConstructorFlavor::SynthesizedDefault);
decl->addMember(ctor);
+ addAutoDiffModifiersToFunc(this, m_astBuilder, ctor);
+ addVisibilityModifier(ctor, ctorVisibility);
return ctor;
}
+static inline bool _isDefaultCtor(ConstructorDecl* ctor)
+{
+ auto allParamHaveInitExpr = [](ConstructorDecl* ctor)
+ {
+ for (auto i : ctor->getParameters())
+ if (!i->initExpr)
+ return false;
+ return true;
+ };
+
+ // 1. default ctor must have no parameters
+ // 2. default ctor can have parameters, but all parameters have init expr (Because we won't
+ // differentiate this case from 2.)
+ if (ctor->members.getCount() == 0 || allParamHaveInitExpr(ctor))
+ {
+ return true;
+ }
+
+ return false;
+}
+
static ConstructorDecl* _getDefaultCtor(StructDecl* structDecl)
{
for (auto ctor : structDecl->getMembersOfType<ConstructorDecl>())
{
- if (!ctor->body || ctor->members.getCount() != 0)
- continue;
- return ctor;
+ if (_isDefaultCtor(ctor))
+ return ctor;
}
return nullptr;
}
@@ -2095,9 +2152,8 @@ static List<ConstructorDecl*> _getCtorList(
if (!ctor || !ctor->body)
return;
ctorList.add(ctor);
- if (ctor->members.getCount() != 0)
- return;
- *defaultCtorOut = ctor;
+ if (_isDefaultCtor(ctor))
+ *defaultCtorOut = ctor;
};
if (ctorLookupResult.items.getCount() == 0)
{
@@ -2208,16 +2264,10 @@ bool isDefaultInitializable(VarDeclBase* varDecl)
return true;
}
-static Expr* constructDefaultInitExprForVar(SemanticsVisitor* visitor, VarDeclBase* varDecl)
+static Expr* constructDefaultConstructorForType(SemanticsVisitor* visitor, Type* type)
{
- if (!varDecl->type || !varDecl->type.type)
- return nullptr;
-
- if (!isDefaultInitializable(varDecl))
- return nullptr;
-
ConstructorDecl* defaultCtor = nullptr;
- auto declRefType = as<DeclRefType>(varDecl->type.type);
+ auto declRefType = as<DeclRefType>(type);
if (declRefType)
{
if (auto structDecl = as<StructDecl>(declRefType->getDeclRef().getDecl()))
@@ -2225,7 +2275,6 @@ static Expr* constructDefaultInitExprForVar(SemanticsVisitor* visitor, VarDeclBa
defaultCtor = _getDefaultCtor(structDecl);
}
}
-
if (defaultCtor)
{
auto* invoke = visitor->getASTBuilder()->create<InvokeExpr>();
@@ -2239,6 +2288,22 @@ static Expr* constructDefaultInitExprForVar(SemanticsVisitor* visitor, VarDeclBa
nullptr);
return invoke;
}
+
+ return nullptr;
+}
+
+static Expr* constructDefaultInitExprForType(SemanticsVisitor* visitor, VarDeclBase* varDecl)
+{
+ if (!varDecl->type || !varDecl->type.type)
+ return nullptr;
+
+ if (!isDefaultInitializable(varDecl))
+ return nullptr;
+
+ if (auto defaultInitExpr = constructDefaultConstructorForType(visitor, varDecl->type.type))
+ {
+ return defaultInitExpr;
+ }
else
{
auto* defaultCall = visitor->getASTBuilder()->create<DefaultConstructExpr>();
@@ -2255,7 +2320,7 @@ void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl)
if (getOptionSet().hasOption(CompilerOptionName::ZeroInitialize) && !varDecl->initExpr &&
as<VarDecl>(varDecl))
{
- varDecl->initExpr = constructDefaultInitExprForVar(this, varDecl);
+ varDecl->initExpr = constructDefaultInitExprForType(this, varDecl);
}
if (auto initExpr = varDecl->initExpr)
@@ -2274,6 +2339,7 @@ void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl)
if (initExpr->type.isWriteOnly)
getSink()->diagnose(initExpr, Diagnostics::readingFromWriteOnly);
+
initExpr = coerce(CoercionSite::Initializer, varDecl->type.Ptr(), initExpr);
varDecl->initExpr = initExpr;
@@ -2361,11 +2427,21 @@ void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl)
// for the variable, that will be used for all downstream
// code generation.
//
- varDecl->initExpr =
- CompleteOverloadCandidate(overloadContext, *overloadContext.bestCandidate);
- getShared()->cacheImplicitCastMethod(
- key,
- ImplicitCastMethod{*overloadContext.bestCandidate, 0});
+ auto constructorDecl =
+ as<ConstructorDecl>(overloadContext.bestCandidate->item.declRef).getDecl();
+ // We don't allow implicit initialization of struct only have synthesized default
+ // ctor.
+ if ((constructorDecl &&
+ !constructorDecl->containsFlavor(
+ ConstructorDecl::ConstructorFlavor::SynthesizedDefault)) ||
+ !constructorDecl)
+ {
+ varDecl->initExpr =
+ CompleteOverloadCandidate(overloadContext, *overloadContext.bestCandidate);
+ getShared()->cacheImplicitCastMethod(
+ key,
+ ImplicitCastMethod{*overloadContext.bestCandidate, 0});
+ }
}
}
}
@@ -2478,18 +2554,18 @@ void SemanticsVisitor::CheckConstraintSubType(TypeExp& typeExp)
}
}
-void addVisibilityModifier(ASTBuilder* builder, Decl* decl, DeclVisibility vis)
+void SemanticsVisitor::addVisibilityModifier(Decl* decl, DeclVisibility vis)
{
switch (vis)
{
case DeclVisibility::Public:
- addModifier(decl, builder->create<PublicModifier>());
+ addModifier(decl, m_astBuilder->create<PublicModifier>());
break;
case DeclVisibility::Internal:
- addModifier(decl, builder->create<InternalModifier>());
+ addModifier(decl, m_astBuilder->create<InternalModifier>());
break;
case DeclVisibility::Private:
- addModifier(decl, builder->create<PrivateModifier>());
+ addModifier(decl, m_astBuilder->create<PrivateModifier>());
break;
default:
break;
@@ -2635,7 +2711,7 @@ bool SemanticsVisitor::trySynthesizeDifferentialAssociatedTypeRequirementWitness
aggTypeDecl->members.add(diffField);
auto visibility = getDeclVisibility(member);
- addVisibilityModifier(m_astBuilder, diffField, visibility);
+ addVisibilityModifier(diffField, visibility);
aggTypeDecl->invalidateMemberDictionary();
@@ -2748,7 +2824,7 @@ bool SemanticsVisitor::trySynthesizeDifferentialAssociatedTypeRequirementWitness
auto requirementVisibility = getDeclVisibility(requirementDeclRef.getDecl());
auto thisVisibility = getDeclVisibility(context->parentDecl);
auto visibility = Math::Min(thisVisibility, requirementVisibility);
- addVisibilityModifier(m_astBuilder, aggTypeDecl, visibility);
+ addVisibilityModifier(aggTypeDecl, visibility);
}
// Synthesize the rest of IDifferential method conformances by recursively checking
@@ -4411,7 +4487,7 @@ void SemanticsVisitor::addModifiersToSynthesizedDecl(
auto requirementVisibility = getDeclVisibility(requiredMemberDeclRef.getDecl());
auto thisVisibility = getDeclVisibility(context->parentDecl);
auto visibility = Math::Min(thisVisibility, requirementVisibility);
- addVisibilityModifier(m_astBuilder, synthesized, visibility);
+ addVisibilityModifier(synthesized, visibility);
}
}
@@ -5318,7 +5394,7 @@ bool SemanticsVisitor::trySynthesizePropertyRequirementWitness(
auto requirementVisibility = getDeclVisibility(requiredMemberDeclRef.getDecl());
auto thisVisibility = getDeclVisibility(context->parentDecl);
auto visibility = Math::Min(thisVisibility, requirementVisibility);
- addVisibilityModifier(m_astBuilder, synPropertyDecl, visibility);
+ addVisibilityModifier(synPropertyDecl, visibility);
}
return true;
}
@@ -5487,7 +5563,7 @@ bool SemanticsVisitor::trySynthesizeWrapperTypePropertyRequirementWitness(
if (innerProperty.getDecl()->findModifier<VisibilityModifier>())
{
auto vis = getDeclVisibility(innerProperty.getDecl());
- addVisibilityModifier(m_astBuilder, synPropertyDecl, vis);
+ addVisibilityModifier(synPropertyDecl, vis);
}
context->parentDecl->addMember(synPropertyDecl);
@@ -5874,7 +5950,7 @@ bool SemanticsVisitor::trySynthesizeWrapperTypeSubscriptRequirementWitness(
auto requirementVisibility = getDeclVisibility(requiredMemberDeclRef.getDecl());
auto thisVisibility = getDeclVisibility(context->parentDecl);
auto visibility = Math::Min(thisVisibility, requirementVisibility);
- addVisibilityModifier(m_astBuilder, synSubscriptDecl, visibility);
+ addVisibilityModifier(synSubscriptDecl, visibility);
}
return true;
@@ -5995,7 +6071,7 @@ bool SemanticsVisitor::trySynthesizeSubscriptRequirementWitness(
auto requirementVisibility = getDeclVisibility(requiredMemberDeclRef.getDecl());
auto thisVisibility = getDeclVisibility(context->parentDecl);
auto visibility = Math::Min(thisVisibility, requirementVisibility);
- addVisibilityModifier(m_astBuilder, synSubscriptDecl, visibility);
+ addVisibilityModifier(synSubscriptDecl, visibility);
}
return true;
@@ -7670,6 +7746,43 @@ void SemanticsVisitor::validateEnumTagType(Type* type, SourceLoc const& loc)
getSink()->diagnose(loc, Diagnostics::invalidEnumTagType, type);
}
+bool SemanticsVisitor::_hasExplicitConstructor(StructDecl* structDecl, bool checkBaseType)
+{
+ if (!structDecl)
+ return false;
+
+ auto _hasExplicitCtor = [](AggTypeDecl* aggDecl) -> bool
+ {
+ // First check if the extension of this struct defines an explicit constructor.
+ for (auto ctor : aggDecl->getMembersOfType<ConstructorDecl>())
+ {
+ // constructor that is not synthesized must be user defined.
+ if (ctor->findModifier<SynthesizedModifier>() == nullptr)
+ {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ if (_hasExplicitCtor(structDecl))
+ return true;
+
+ if (!checkBaseType)
+ return false;
+
+ for (auto inheritanceMember : structDecl->getMembersOfType<InheritanceDecl>())
+ {
+ auto baseTypeDecl = isDeclRefTypeOf<AggTypeDecl>(inheritanceMember->base.type);
+ if (baseTypeDecl && !as<InterfaceDecl>(baseTypeDecl))
+ {
+ if (_hasExplicitCtor(baseTypeDecl.getDecl()))
+ return true;
+ }
+ }
+ return false;
+}
+
void SemanticsDeclBasesVisitor::visitEnumDecl(EnumDecl* decl)
{
SLANG_OUTER_SCOPE_CONTEXT_DECL_RAII(this, decl);
@@ -9064,31 +9177,97 @@ static SeqStmt* _ensureCtorBodyIsSeqStmt(ASTBuilder* m_astBuilder, ConstructorDe
return as<SeqStmt>(stmt->body);
}
+MemberExpr* SemanticsDeclBodyVisitor::createMemberExpr(
+ ThisExpr* thisExpr,
+ Scope* scope,
+ Decl* member)
+{
+ MemberExpr* memberExpr = m_astBuilder->create<MemberExpr>();
+ memberExpr->baseExpression = thisExpr;
+ memberExpr->declRef = member->getDefaultDeclRef();
+ memberExpr->scope = scope;
+ memberExpr->loc = member->loc;
+ memberExpr->name = member->getName();
+ memberExpr->type = GetTypeForDeclRef(member->getDefaultDeclRef(), member->loc);
+
+ return memberExpr;
+}
+
+Expr* SemanticsDeclBodyVisitor::createCtorParamExpr(ConstructorDecl* ctor, Index paramIndex)
+{
+ if (paramIndex < ctor->members.getCount())
+ {
+ if (auto param = as<ParamDecl>(ctor->members[paramIndex]))
+ {
+ auto paramType = param->getType();
+ auto paramExpr = m_astBuilder->create<VarExpr>();
+ paramExpr->scope = ctor->ownedScope;
+ paramExpr->declRef = param;
+ paramExpr->type = paramType;
+ paramExpr->loc = param->loc;
+ return paramExpr;
+ }
+ }
+ return nullptr;
+}
+
void SemanticsDeclBodyVisitor::synthesizeCtorBodyForBases(
ConstructorDecl* ctor,
List<DeclAndCtorInfo>& inheritanceDefaultCtorList,
ThisExpr* thisExpr,
- SeqStmt* seqStmtChild)
+ SeqStmt* seqStmtChild,
+ bool isMemberInitCtor,
+ Index& ioParamIndex)
{
- // e.g. this->base = BaseType();
for (auto& declInfo : inheritanceDefaultCtorList)
{
- if (!declInfo.defaultCtor)
- continue;
+ ConstructorDecl* baseCtor = nullptr;
+ List<Expr*> argumentList;
+
+ if (isMemberInitCtor)
+ {
+ // Pick the parameters from the member initialize ctor, and use them to invoke the
+ // base's member initialize ctor. e.g. base->init(...);
+ baseCtor = _getSynthesizedConstructor(
+ declInfo.parent,
+ ConstructorDecl::ConstructorFlavor::SynthesizedMemberInit);
+ if (baseCtor)
+ {
+ Index idx = 0;
+ for (; idx < baseCtor->getParameters().getCount(); idx++)
+ {
+ auto paramExpr = createCtorParamExpr(ctor, idx);
+ argumentList.add(paramExpr);
+ }
+ ioParamIndex += idx;
+ }
+ }
+
+ // It's possible that the base type doesn't have a member initialize ctor, in this case, we
+ // should use the default ctor.
+ if (!baseCtor)
+ {
+ // If the base type has no default constructor, it means that it's not default
+ // initializable, e.g. unsized array, resource type, etc. We will not synthesize code to
+ // initialize it.
+ if (!declInfo.defaultCtor)
+ continue;
+ baseCtor = declInfo.defaultCtor;
+ }
auto declRefType = as<DeclRefType>(declInfo.type);
auto ctorToInvoke = m_astBuilder->create<VarExpr>();
ctorToInvoke->declRef = declRefType->getDeclRef();
- ctorToInvoke->name = declInfo.defaultCtor->getName();
- ctorToInvoke->loc = declInfo.defaultCtor->loc;
+ ctorToInvoke->name = baseCtor->getName();
+ ctorToInvoke->loc = baseCtor->loc;
ctorToInvoke->type = m_astBuilder->getFuncType(ArrayView<Type*>(), declRefType);
auto invoke = m_astBuilder->create<InvokeExpr>();
invoke->functionExpr = ctorToInvoke;
+ invoke->arguments.addRange(argumentList);
auto assign = m_astBuilder->create<AssignExpr>();
-
assign->left = coerce(CoercionSite::Initializer, declRefType, thisExpr);
assign->right = invoke;
@@ -9105,25 +9284,57 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBodyForMember(
Decl* member,
ThisExpr* thisExpr,
Dictionary<Decl*, Expr*>& cachedDeclToCheckedVar,
- SeqStmt* seqStmtChild)
+ SeqStmt* seqStmtChild,
+ bool isMemberInitCtor,
+ Index& paramIndex)
{
auto varDeclBase = as<VarDeclBase>(member);
// Static variables are initialized at start of runtime, not inside a constructor
- if (!varDeclBase || !varDeclBase->initExpr || varDeclBase->hasModifier<HLSLStaticModifier>())
+ // Once thing to notice is that if a member variable doesn't have name, it must be synthesized
+ // instead of defined by user, we should not put it into the constructor because it's not a real
+ // member.
+ if (!varDeclBase || varDeclBase->hasModifier<HLSLStaticModifier>() ||
+ varDeclBase->getName() == nullptr)
return;
- MemberExpr* memberExpr = m_astBuilder->create<MemberExpr>();
- memberExpr->baseExpression = thisExpr;
- memberExpr->declRef = member->getDefaultDeclRef();
- memberExpr->scope = ctor->ownedScope;
- memberExpr->loc = member->loc;
- memberExpr->name = member->getName();
- memberExpr->type = DeclRefType::create(getASTBuilder(), member->getDefaultDeclRef());
+ Expr* initExpr = nullptr;
+ auto structDecl = as<StructDecl>(member->parentDecl);
+ bool useParamList = isMemberInitCtor;
+ useParamList = isMemberInitCtor && structDecl->m_membersVisibleInCtor.contains(varDeclBase);
+
+ if (!useParamList)
+ {
+ // If this is not a synthesized constructor (e.g. explicit ctor), or
+ // the member has no visibility, we can only use it's init expression to initialize it.
+ if (!varDeclBase->initExpr)
+ return;
+ initExpr = varDeclBase->initExpr;
+ }
+ else
+ {
+ // Find the corresponding parameter, if we can't find it, there
+ // must be something wrong, it indicates that the ctor signature
+ // is incorrect that the parameter list doesn't match the member list.
+ initExpr = createCtorParamExpr(ctor, paramIndex++);
+ if (!initExpr)
+ {
+ const char* structName =
+ (structDecl->getName() ? structDecl->getName()->text.begin() : "unknown");
+ StringBuilder msg;
+ msg << "Fail to synthesize the member initialize constructor for struct '" << structName
+ << "', the parameter list doesn't match the member list.";
+ SLANG_ABORT_COMPILATION(msg.produceString().begin());
+ }
+ }
+
+ MemberExpr* memberExpr = createMemberExpr(thisExpr, ctor->ownedScope, member);
+ if (!memberExpr->type.isLeftValue)
+ return;
auto assign = m_astBuilder->create<AssignExpr>();
assign->left = memberExpr;
- assign->right = varDeclBase->initExpr;
+ assign->right = initExpr;
assign->loc = member->loc;
auto stmt = m_astBuilder->create<ExpressionStmt>();
@@ -9139,9 +9350,6 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBodyForMember(
cachedDeclToCheckedVar.add({member, checkedMemberVarExpr});
}
- if (!checkedMemberVarExpr->type.isLeftValue)
- return;
-
seqStmtChild->stmts.add(stmt);
}
@@ -9163,14 +9371,37 @@ void SemanticsDeclBodyVisitor::synthesizeCtorBody(
thisExpr->scope = ctor->ownedScope;
thisExpr->type = ctor->returnType.type;
- // Initialize base type by using its default constructor if it has one.
- synthesizeCtorBodyForBases(ctor, inheritanceDefaultCtorList, thisExpr, seqStmtChild);
-
- // Initialize member variables by using their default value if they have one
- // e.g. this->member = default_value
+ // We treat the ctor with parameters and all parameters have default value as default ctor
+ // as well, but the method to synthesize them are totally different, therefore, we need to
+ // differentiate them here.
+ bool isMemberInitCtor =
+ ctor->containsFlavor(ConstructorDecl::ConstructorFlavor::SynthesizedMemberInit);
+
+ // When we synthesize the member initialize constructor, we need to use the parameters in
+ // the function body, so this inout parameter is used to keep track of the index of the
+ // parameters.
+ Index ioParamIndex = 0;
+
+ // The first step is to synthesize the initialization of the base member.
+ synthesizeCtorBodyForBases(
+ ctor,
+ inheritanceDefaultCtorList,
+ thisExpr,
+ seqStmtChild,
+ isMemberInitCtor,
+ ioParamIndex);
+
+ // Then synthesize the initialization of the other members.
for (auto& m : structDecl->members)
{
- synthesizeCtorBodyForMember(ctor, m, thisExpr, cachedDeclToCheckedVar, seqStmtChild);
+ synthesizeCtorBodyForMember(
+ ctor,
+ m,
+ thisExpr,
+ cachedDeclToCheckedVar,
+ seqStmtChild,
+ isMemberInitCtor,
+ ioParamIndex);
}
if (seqStmtChild->stmts.getCount() != 0)
@@ -9224,7 +9455,7 @@ void SemanticsDeclBodyVisitor::visitAggTypeDecl(AggTypeDecl* aggTypeDecl)
ensureDecl(m->getDefaultDeclRef(), DeclCheckState::DefaultConstructorReadyForUse);
if (!isDefaultInitializableType || varDeclBase->initExpr)
continue;
- varDeclBase->initExpr = constructDefaultInitExprForVar(this, varDeclBase);
+ varDeclBase->initExpr = constructDefaultInitExprForType(this, varDeclBase);
}
synthesizeCtorBody(structDeclInfo, inheritanceDefaultCtorList, structDecl);
@@ -9776,7 +10007,7 @@ Type* SemanticsVisitor::findResultTypeForConstructorDecl(ConstructorDecl* decl)
void SemanticsDeclHeaderVisitor::visitConstructorDecl(ConstructorDecl* decl)
{
- // We need to compute the result tyep for this declaration,
+ // We need to compute the result type for this declaration,
// since it wasn't filled in for us.
decl->returnType.type = findResultTypeForConstructorDecl(decl);
@@ -11976,13 +12207,174 @@ void SemanticsDeclAttributesVisitor::checkPrimalSubstituteOfAttribute(
DeclAssociationKind::PrimalSubstituteFunc);
}
+bool SemanticsDeclAttributesVisitor::collectInitializableMembers(
+ StructDecl* structDecl,
+ const DeclVisibility ctorVisibility,
+ List<VarDeclBase*>& resultMembers)
+{
+ auto findMembers = [&](StructDecl* structDecl)
+ {
+ for (auto varDeclRef : getMembersOfType<VarDeclBase>(
+ getASTBuilder(),
+ structDecl,
+ MemberFilterStyle::Instance))
+ {
+ auto varDecl = varDeclRef.getDecl();
+ if (getDeclVisibility(varDecl) < ctorVisibility)
+ continue;
+
+ auto type = GetTypeForDeclRef(varDeclRef, varDecl->loc);
+ if (!type.isLeftValue)
+ continue;
+
+ resultMembers.add(varDecl);
+ structDecl->m_membersVisibleInCtor.add(varDecl);
+ }
+ };
+
+ // Find the base type's members first
+ for (auto inheritanceMember : structDecl->getMembersOfType<InheritanceDecl>())
+ {
+ // For base types, we need to pick their parameters of the constructor to the derived type's
+ // constructor
+ if (auto baseTypeDeclRef = isDeclRefTypeOf<StructDecl>(inheritanceMember->base.type))
+ {
+ // We should only find the member initialization constructor because it is the
+ // constructor has parameters
+ ConstructorDecl* ctor = _getSynthesizedConstructor(
+ baseTypeDeclRef.getDecl(),
+ ConstructorDecl::ConstructorFlavor::SynthesizedMemberInit);
+
+ // The constructor has to have higher or equal visibility level than the struct itself,
+ // otherwise, it's not accessible so we will not pick up.
+ if (ctor && getDeclVisibility(ctor) >= ctorVisibility)
+ {
+ for (ParamDecl* param : ctor->getParameters())
+ {
+ // Because the parameters in the ctor must have the higher or equal visibility
+ // than the ctor itself, we don't need to check the visibility level of the
+ // parameter.
+ resultMembers.add(param);
+ }
+ }
+ }
+ }
+
+ // Find the struct's members
+ findMembers(structDecl);
+ return (resultMembers.getCount() > 0);
+}
+
+// If a struct's member:
+// 1. has an initialize expression: Struct S {int a = 1;}; or
+// 2. is a default initializable type
+// Note, If a type is not default initializable, it doesn't have default value.
+// it can be associated with default value expression in the constructor signature.
+// This function helps to check whether either of those 2 conditions are met and create
+// a default value for the parameter.
+// It's totally fine that there is no default value for the parameter, in this case, user
+// code has to provide the argument for this parameter.
+static Expr* _getParamDefaultValue(SemanticsVisitor* visitor, VarDeclBase* varDecl)
+{
+ // 1st condition is easy, we can just use the init expression as the default value.
+ if (varDecl->initExpr)
+ {
+ return varDecl->initExpr;
+ }
+
+ if (!varDecl->type || !varDecl->type.type)
+ return nullptr;
+
+ if (!isDefaultInitializable(varDecl))
+ return nullptr;
+
+ return constructDefaultConstructorForType(visitor, varDecl->type.type);
+}
+
+bool SemanticsDeclAttributesVisitor::_synthesizeCtorSignature(StructDecl* structDecl)
+{
+ // If a type or its base type already defines any explicit constructors, do not synthesize any
+ // constructors. see:
+ // https://github.com/shader-slang/slang/blob/master/docs/proposals/004-initialization.md#inheritance-initialization
+ if (_hasExplicitConstructor(structDecl, true))
+ return false;
+
+ // synthesize the signature first.
+ // The constructor's visibility level is the same as the struct itself.
+ // See:
+ // https://github.com/shader-slang/slang/blob/master/docs/proposals/004-initialization.md#synthesis-of-constructors-for-member-initialization
+ DeclVisibility ctorVisibility = getDeclVisibility(structDecl);
+
+ // Only the members whose visibility level is higher or equal than the
+ // constructor's visibility level will appear in the constructor's parameter list.
+ List<VarDeclBase*> resultMembers;
+ if (!collectInitializableMembers(structDecl, ctorVisibility, resultMembers))
+ return false;
+
+ // synthesize the constructor signature:
+ // 1. The constructor's name is always `$init`, we create one without parameters now.
+ ConstructorDecl* ctor = createCtor(structDecl, ctorVisibility);
+ ctor->addFlavor(ConstructorDecl::ConstructorFlavor::SynthesizedMemberInit);
+
+ ctor->members.reserve(resultMembers.getCount());
+
+ // 2. Add the parameter list
+ bool stopProcessingDefaultValues = false;
+ for (SlangInt i = resultMembers.getCount() - 1; i >= 0; i--)
+ {
+ auto member = resultMembers[i];
+ auto parentAggDecl = getParentAggTypeDecl(member);
+ ;
+
+ auto ctorParam = m_astBuilder->create<ParamDecl>();
+ ctorParam->type = (TypeExp)member->type;
+
+ if (!stopProcessingDefaultValues)
+ ctorParam->initExpr = _getParamDefaultValue(this, member);
+
+ if (!ctorParam->initExpr)
+ stopProcessingDefaultValues = true;
+
+ ctorParam->parentDecl = ctor;
+
+ Name* paramName =
+ (parentAggDecl == structDecl)
+ ? member->getName()
+ : getName(parentAggDecl->getName()->text + "_" + member->getName()->text);
+
+ ctorParam->nameAndLoc = NameLoc(paramName, ctor->loc);
+
+ ctorParam->loc = ctor->loc;
+ ctor->members.add(ctorParam);
+
+ // We need to ensure member is `no_diff` if it cannot be differentiated, `ctor` modifiers do
+ // not matter in this case since member-wise ctor is always differentiable or "treat as
+ // differentiable".
+ if (!isTypeDifferentiable(member->getType()) || member->hasModifier<NoDiffModifier>())
+ {
+ auto noDiffMod = m_astBuilder->create<NoDiffModifier>();
+ noDiffMod->loc = ctorParam->loc;
+ addModifier(ctorParam, noDiffMod);
+ }
+ }
+ ctor->members.reverse();
+ return true;
+}
+
void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl)
{
- // add a empty deault CTor if missing; checking in attributes
- // to avoid circular checking logic
- auto defaultCtor = _getDefaultCtor(structDecl);
- if (!defaultCtor)
- _createCtor(this, m_astBuilder, structDecl);
+ // add the member initialize constructor here to avoid circular checking logic
+ if (!_synthesizeCtorSignature(structDecl))
+ {
+ // add a default CTor if missing; checking in attributes
+ // to avoid circular checking logic
+ auto defaultCtor = _getDefaultCtor(structDecl);
+ if (!defaultCtor)
+ {
+ DeclVisibility ctorVisibility = getDeclVisibility(structDecl);
+ createCtor(structDecl, ctorVisibility);
+ }
+ }
int backingWidth = 0;
[[maybe_unused]] int totalWidth = 0;