summaryrefslogtreecommitdiffstats
path: root/source
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
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')
-rw-r--r--source/slang/core.meta.slang2
-rw-r--r--source/slang/hlsl.meta.slang23
-rw-r--r--source/slang/slang-ast-decl.h21
-rw-r--r--source/slang/slang-ast-expr.h7
-rw-r--r--source/slang/slang-check-conversion.cpp243
-rw-r--r--source/slang/slang-check-decl.cpp540
-rw-r--r--source/slang/slang-check-expr.cpp4
-rw-r--r--source/slang/slang-check-impl.h28
-rw-r--r--source/slang/slang-check-overload.cpp19
-rw-r--r--source/slang/slang-ir-check-differentiability.cpp12
-rw-r--r--source/slang/slang-lower-to-ir.cpp5
-rw-r--r--source/slang/slang-options.cpp15
12 files changed, 818 insertions, 101 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 19421735c..b7f50cd1b 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -1749,6 +1749,8 @@ struct NativeString
__implicit_conversion($(kConversionCost_None))
__intrinsic_op($(kIROp_getNativeStr))
__init(String value);
+
+ __init() { this = NativeString(""); }
};
extension Ptr<void>
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 3baad5c10..2399df254 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -21107,11 +21107,12 @@ ${
case hlsl:
uint isSingleLod = 0;
- Footprint footprint = {__queryFootprint$(CoarseOrFine)NVAPI(
+ __queryFootprint$(CoarseOrFine)NVAPI(
Shape.dimensions,
__getRegisterSpace(this), __getRegisterIndex(this),
__getRegisterSpace(sampler), __getRegisterIndex(sampler),
- __vectorReshape<3>(coords), granularity, /* out */isSingleLod), false};
+ __vectorReshape<3>(coords), granularity, /* out */isSingleLod);
+ Footprint footprint = {false};
footprint._isSingleLevel = (isSingleLod != 0);
return footprint;
}
@@ -21140,12 +21141,12 @@ ${
return footprint;
case hlsl:
uint isSingleLod = 0;
- Footprint footprint = {__queryFootprint$(CoarseOrFine)BiasNVAPI(
+ __queryFootprint$(CoarseOrFine)BiasNVAPI(
Shape.dimensions,
__getRegisterSpace(this), __getRegisterIndex(this),
__getRegisterSpace(sampler), __getRegisterIndex(sampler),
- __vectorReshape<3>(coords), granularity, lodBias, /* out */isSingleLod), false};
-
+ __vectorReshape<3>(coords), granularity, lodBias, /* out */isSingleLod);
+ Footprint footprint = {false};
footprint._isSingleLevel = (isSingleLod != 0);
return footprint;
}
@@ -21223,12 +21224,12 @@ ${
return footprint;
case hlsl:
uint isSingleLod = 0;
- Footprint footprint = {__queryFootprint$(CoarseOrFine)LevelNVAPI(
+ __queryFootprint$(CoarseOrFine)LevelNVAPI(
Shape.dimensions,
__getRegisterSpace(this), __getRegisterIndex(this),
__getRegisterSpace(sampler), __getRegisterIndex(sampler),
- __vectorReshape<3>(coords), granularity, lod, /* out */isSingleLod), false};
-
+ __vectorReshape<3>(coords), granularity, lod, /* out */isSingleLod);
+ Footprint footprint = {false};
footprint._isSingleLevel = (isSingleLod != 0);
return footprint;
}
@@ -21262,12 +21263,12 @@ ${{{
return footprint;
case hlsl:
uint isSingleLod = 0;
- Footprint footprint = {__queryFootprint$(CoarseOrFine)GradNVAPI(
+ __queryFootprint$(CoarseOrFine)GradNVAPI(
Shape.dimensions,
__getRegisterSpace(this), __getRegisterIndex(this),
__getRegisterSpace(sampler), __getRegisterIndex(sampler),
- __vectorReshape<3>(coords), granularity, __vectorReshape<3>(dx), __vectorReshape<3>(dy), /* out */isSingleLod), false};
-
+ __vectorReshape<3>(coords), granularity, __vectorReshape<3>(dx), __vectorReshape<3>(dy), /* out */isSingleLod);
+ Footprint footprint = {false};
footprint._isSingleLevel = (isSingleLod != 0);
return footprint;
}
diff --git a/source/slang/slang-ast-decl.h b/source/slang/slang-ast-decl.h
index 7feb70e6a..ff8e5684a 100644
--- a/source/slang/slang-ast-decl.h
+++ b/source/slang/slang-ast-decl.h
@@ -165,6 +165,10 @@ class AggTypeDecl : public AggTypeDeclBase
class StructDecl : public AggTypeDecl
{
SLANG_AST_CLASS(StructDecl);
+
+ SLANG_UNREFLECTED
+ // We will use these auxiliary to help in synthesizing the member initialize constructor.
+ Slang::HashSet<VarDeclBase*> m_membersVisibleInCtor;
};
class ClassDecl : public AggTypeDecl
@@ -374,9 +378,20 @@ class ConstructorDecl : public FunctionDeclBase
{
SLANG_AST_CLASS(ConstructorDecl)
- // Indicates whether the declaration was synthesized by
- // slang and not actually provided by the user
- bool isSynthesized = false;
+ enum class ConstructorFlavor : int
+ {
+ UserDefined = 0x00,
+ // Indicates whether the declaration was synthesized by
+ // Slang and not explicitly provided by the user
+ SynthesizedDefault = 0x01,
+ // Member initialize constructor is a synthesized ctor,
+ // but it takes parameters.
+ SynthesizedMemberInit = 0x02
+ };
+
+ int m_flavor = (int)ConstructorFlavor::UserDefined;
+ void addFlavor(ConstructorFlavor flavor) { m_flavor |= (int)flavor; }
+ bool containsFlavor(ConstructorFlavor flavor) { return m_flavor & (int)flavor; }
};
// A subscript operation used to index instances of a type
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index 409909b16..c9bc86b79 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -135,6 +135,8 @@ class InitializerListExpr : public Expr
{
SLANG_AST_CLASS(InitializerListExpr)
List<Expr*> args;
+
+ bool useCStyleInitialization = true;
};
class GetArrayLengthExpr : public Expr
@@ -193,6 +195,11 @@ class InvokeExpr : public AppExprBase
SLANG_AST_CLASS(InvokeExpr)
};
+class ExplicitCtorInvokeExpr : public InvokeExpr
+{
+ SLANG_AST_CLASS(ExplicitCtorInvokeExpr)
+};
+
enum class TryClauseType
{
None,
diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp
index db8548dce..d89997bad 100644
--- a/source/slang/slang-check-conversion.cpp
+++ b/source/slang/slang-check-conversion.cpp
@@ -205,6 +205,234 @@ DeclRef<StructDecl> findBaseStructDeclRef(
return baseStructDeclRef;
}
+ConstructorDecl* SemanticsVisitor::_getSynthesizedConstructor(
+ StructDecl* structDecl,
+ ConstructorDecl::ConstructorFlavor flavor)
+{
+ for (auto ctor : structDecl->getMembersOfType<ConstructorDecl>())
+ {
+ if (ctor->containsFlavor(flavor))
+ return ctor;
+ }
+ return nullptr;
+}
+
+bool SemanticsVisitor::isCStyleType(Type* type, HashSet<Type*>& isVisit)
+{
+ isVisit.add(type);
+ auto cacheResult = [&](bool result)
+ {
+ getShared()->cacheCStyleType(type, result);
+ return result;
+ };
+
+ // Check cache first
+ if (bool* isCStyle = getShared()->isCStyleType(type))
+ {
+ return *isCStyle;
+ }
+
+ // 1. It has to be basic scalar, vector or matrix type, or user-defined struct.
+ if (as<VectorExpressionType>(type) || as<MatrixExpressionType>(type) ||
+ as<BasicExpressionType>(type) || isDeclRefTypeOf<EnumDecl>(type).getDecl())
+ return cacheResult(true);
+
+
+ if (auto structDecl = isDeclRefTypeOf<StructDecl>(type).getDecl())
+ {
+ // 2. It cannot have inheritance, but inherit from interface is fine.
+ for (auto inheritanceDecl : structDecl->getMembersOfType<InheritanceDecl>())
+ {
+ if (!isDeclRefTypeOf<InterfaceDecl>(inheritanceDecl->base.type))
+ {
+ return cacheResult(false);
+ }
+ }
+
+ // 3. It cannot have explicit constructor
+ if (_hasExplicitConstructor(structDecl, true))
+ return cacheResult(false);
+
+ // 4. All of its members have to have the same visibility as the struct itself.
+ DeclVisibility structVisibility = getDeclVisibility(structDecl);
+ for (auto varDecl : structDecl->getMembersOfType<VarDeclBase>())
+ {
+ if (getDeclVisibility(varDecl) != structVisibility)
+ {
+ return cacheResult(false);
+ }
+ }
+
+ for (auto varDecl : structDecl->getMembersOfType<VarDeclBase>())
+ {
+ Type* varType = varDecl->getType();
+
+ if (isDeclRefTypeOf<StructDecl>(varType))
+ {
+ // Avoid infinite loop in case of circular reference.
+ if (isVisit.contains(varType))
+ continue;
+ }
+
+ // Recursively check the type of the member.
+ if (!isCStyleType(varType, isVisit))
+ return cacheResult(false);
+ }
+ }
+
+ // 5. All its members are legacy C-Style structs or arrays of legacy C-style structs
+ if (auto arrayType = as<ArrayExpressionType>(type))
+ {
+ if (arrayType->isUnsized())
+ {
+ return cacheResult(false);
+ }
+
+ auto elementType = arrayType->getElementType();
+ if (isDeclRefTypeOf<StructDecl>(elementType))
+ {
+ // Avoid infinite loop in case of circular reference.
+ if (isVisit.contains(elementType))
+ cacheResult(true);
+ }
+
+ if (!isCStyleType(elementType, isVisit))
+ return cacheResult(false);
+ }
+ return cacheResult(true);
+}
+
+Expr* SemanticsVisitor::_createCtorInvokeExpr(
+ Type* toType,
+ const SourceLoc& loc,
+ const List<Expr*>& coercedArgs)
+{
+ auto* varExpr = getASTBuilder()->create<VarExpr>();
+ varExpr->type = (QualType)getASTBuilder()->getTypeType(toType);
+ varExpr->declRef = isDeclRefTypeOf<Decl>(toType);
+
+ auto* constructorExpr = getASTBuilder()->create<ExplicitCtorInvokeExpr>();
+ constructorExpr->functionExpr = varExpr;
+ constructorExpr->arguments.addRange(coercedArgs);
+ constructorExpr->loc = loc;
+
+ return constructorExpr;
+}
+
+// translation from initializer list to constructor invocation if the struct has constructor.
+bool SemanticsVisitor::createInvokeExprForExplicitCtor(
+ Type* toType,
+ InitializerListExpr* fromInitializerListExpr,
+ Expr** outExpr)
+{
+ if (auto toStructDeclRef = isDeclRefTypeOf<StructDecl>(toType))
+ {
+ // TODO: This is just a special case for a backwards-compatibility feature
+ // for HLSL, this flag will imply that the initializer list is synthesized
+ // for a type cast from a literal zero to a 'struct'. In this case, we will fall
+ // back to legacy initializer list logic.
+ if (!fromInitializerListExpr->useCStyleInitialization)
+ {
+ HashSet<Type*> isVisit;
+ if (!isCStyleType(toType, isVisit))
+ return false;
+ }
+
+ if (_hasExplicitConstructor(toStructDeclRef.getDecl(), false))
+ {
+ auto ctorInvokeExpr = _createCtorInvokeExpr(
+ toType,
+ fromInitializerListExpr->loc,
+ fromInitializerListExpr->args);
+
+ DiagnosticSink tempSink(getSourceManager(), nullptr);
+ SemanticsVisitor subVisitor(withSink(&tempSink));
+ ctorInvokeExpr = subVisitor.CheckTerm(ctorInvokeExpr);
+
+ if (tempSink.getErrorCount())
+ {
+ HashSet<Type*> isVisit;
+ if (!isCStyleType(toType, isVisit))
+ {
+ Slang::ComPtr<ISlangBlob> blob;
+ tempSink.getBlobIfNeeded(blob.writeRef());
+ getSink()->diagnoseRaw(
+ Severity::Error,
+ static_cast<char const*>(blob->getBufferPointer()));
+ }
+ return false;
+ }
+
+ if (outExpr)
+ {
+ *outExpr = ctorInvokeExpr;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool SemanticsVisitor::createInvokeExprForSynthesizedCtor(
+ Type* toType,
+ InitializerListExpr* fromInitializerListExpr,
+ Expr** outExpr)
+{
+ StructDecl* structDecl = isDeclRefTypeOf<StructDecl>(toType).getDecl();
+
+ if (!structDecl || !_getSynthesizedConstructor(
+ structDecl,
+ ConstructorDecl::ConstructorFlavor::SynthesizedDefault))
+ return false;
+
+ HashSet<Type*> isVisit;
+ bool isCStyle = isCStyleType(toType, isVisit);
+
+ // TODO: This is just a special case for a backwards-compatibility feature
+ // for HLSL, this flag will imply that the initializer list is synthesized
+ // for a type cast from a literal zero to a 'struct'. In this case, we will fall
+ // back to legacy initializer list logic.
+ if (!fromInitializerListExpr->useCStyleInitialization)
+ {
+ if (isCStyle)
+ return false;
+ }
+
+ DiagnosticSink tempSink(getSourceManager(), nullptr);
+ SemanticsVisitor subVisitor(withSink(&tempSink));
+
+ // First make sure the struct is fully checked, otherwise the synthesized constructor may not be
+ // created yet.
+ subVisitor.ensureDecl(structDecl, DeclCheckState::DefinitionChecked);
+
+ List<Expr*> coercedArgs;
+ auto ctorInvokeExpr =
+ _createCtorInvokeExpr(toType, fromInitializerListExpr->loc, fromInitializerListExpr->args);
+
+ ctorInvokeExpr = subVisitor.CheckExpr(ctorInvokeExpr);
+
+ if (ctorInvokeExpr)
+ {
+ if (!tempSink.getErrorCount())
+ {
+ if (outExpr)
+ *outExpr = ctorInvokeExpr;
+
+ return true;
+ }
+ else if (!isCStyle)
+ {
+ Slang::ComPtr<ISlangBlob> blob;
+ tempSink.getBlobIfNeeded(blob.writeRef());
+ getSink()->diagnoseRaw(
+ Severity::Error,
+ static_cast<char const*>(blob->getBufferPointer()));
+ return false;
+ }
+ }
+ return false;
+}
+
bool SemanticsVisitor::_readAggregateValueFromInitializerList(
Type* inToType,
Expr** outToExpr,
@@ -603,6 +831,21 @@ bool SemanticsVisitor::_coerceInitializerList(
!canCoerce(toType, fromInitializerListExpr->type, nullptr))
return _failedCoercion(toType, outToExpr, fromInitializerListExpr);
+ // Try to invoke the user-defined constructor if it exists. This call will
+ // report error diagnostics if the used-defined constructor exists but does not
+ // match the initialize list.
+ if (createInvokeExprForExplicitCtor(toType, fromInitializerListExpr, outToExpr))
+ {
+ return true;
+ }
+
+ // Try to invoke the synthesized constructor if it exists
+ if (createInvokeExprForSynthesizedCtor(toType, fromInitializerListExpr, outToExpr))
+ {
+ return true;
+ }
+
+ // We will fall back to the legacy logic of initialize list.
if (!_readAggregateValueFromInitializerList(
toType,
outToExpr,
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;
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index 135632ba8..7726fd6c8 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -2909,7 +2909,7 @@ Expr* SemanticsExprVisitor::convertToLogicOperatorExpr(InvokeExpr* expr)
if (auto varExpr = as<VarExpr>(expr->functionExpr))
{
- if ((varExpr->name->text == "&&") || (varExpr->name->text == "||"))
+ if ((getText(varExpr->name) == "&&") || (getText(varExpr->name) == "||"))
{
// We only use short-circuiting in scalar input, will fall back
// to non-short-circuiting in vector input.
@@ -3790,8 +3790,10 @@ Expr* SemanticsExprVisitor::visitTypeCastExpr(TypeCastExpr* expr)
InitializerListExpr* initListExpr =
m_astBuilder->create<InitializerListExpr>();
initListExpr->loc = expr->loc;
+ initListExpr->useCStyleInitialization = false;
auto checkedInitListExpr = visitInitializerListExpr(initListExpr);
+
return coerce(CoercionSite::General, typeExp.type, checkedInitListExpr);
}
}
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 638706814..3276b7534 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -760,6 +760,12 @@ public:
m_mapTypePairToImplicitCastMethod[key] = candidate;
}
+ bool* isCStyleType(Type* type) { return m_isCStyleTypeCache.tryGetValue(type); }
+
+ void cacheCStyleType(Type* type, bool isCStyle)
+ {
+ m_isCStyleTypeCache.addIfNotExists(type, isCStyle);
+ }
// Get the inner most generic decl that a decl-ref is dependent on.
// For example, `Foo<T>` depends on the generic decl that defines `T`.
//
@@ -889,6 +895,7 @@ private:
Dictionary<DeclRef<Decl>, InheritanceInfo> m_mapDeclRefToInheritanceInfo;
Dictionary<TypePair, SubtypeWitness*> m_mapTypePairToSubtypeWitness;
Dictionary<ImplicitCastMethodKey, ImplicitCastMethod> m_mapTypePairToImplicitCastMethod;
+ Dictionary<Type*, bool> m_isCStyleTypeCache;
};
/// Local/scoped state of the semantic-checking system
@@ -2780,6 +2787,25 @@ public:
void suggestCompletionItems(
CompletionSuggestions::ScopeKind scopeKind,
LookupResult const& lookupResult);
+
+ bool createInvokeExprForExplicitCtor(
+ Type* toType,
+ InitializerListExpr* fromInitializerListExpr,
+ Expr** outExpr);
+
+ bool createInvokeExprForSynthesizedCtor(
+ Type* toType,
+ InitializerListExpr* fromInitializerListExpr,
+ Expr** outExpr);
+
+ Expr* _createCtorInvokeExpr(Type* toType, const SourceLoc& loc, const List<Expr*>& coercedArgs);
+ bool _hasExplicitConstructor(StructDecl* structDecl, bool checkBaseType);
+ ConstructorDecl* _getSynthesizedConstructor(
+ StructDecl* structDecl,
+ ConstructorDecl::ConstructorFlavor flavor);
+ bool isCStyleType(Type* type, HashSet<Type*>& isVisit);
+
+ void addVisibilityModifier(Decl* decl, DeclVisibility vis);
};
@@ -2999,6 +3025,8 @@ struct SemanticsDeclVisitorBase : public SemanticsVisitor
}
void checkModule(ModuleDecl* programNode);
+
+ ConstructorDecl* createCtor(AggTypeDecl* decl, DeclVisibility ctorVisibility);
};
bool isUnsizedArrayType(Type* type);
diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp
index bca2f7587..b944d2bf4 100644
--- a/source/slang/slang-check-overload.cpp
+++ b/source/slang/slang-check-overload.cpp
@@ -1313,6 +1313,21 @@ int SemanticsVisitor::CompareLookupResultItems(
bool rightIsExtension = as<ExtensionDecl>(rightDeclRefParent.getDecl()) != nullptr;
if (leftIsExtension != rightIsExtension)
{
+ // Add a special case for constructors, where we prefer the one that is not synthesized,
+ if (auto leftCtor = as<ConstructorDecl>(left.declRef.getDecl()))
+ {
+ auto rightCtor = as<ConstructorDecl>(right.declRef.getDecl());
+ bool leftIsSynthesized =
+ leftCtor->containsFlavor(ConstructorDecl::ConstructorFlavor::SynthesizedDefault);
+ bool rightIsSynthesized =
+ rightCtor->containsFlavor(ConstructorDecl::ConstructorFlavor::SynthesizedDefault);
+
+ if (leftIsSynthesized != rightIsSynthesized)
+ {
+ return int(leftIsSynthesized) - int(rightIsSynthesized);
+ }
+ }
+
return int(leftIsExtension) - int(rightIsExtension);
}
else if (leftIsExtension)
@@ -2177,7 +2192,6 @@ void SemanticsVisitor::AddTypeOverloadCandidates(Type* type, OverloadResolveCont
context.sourceScope,
LookupMask::Default,
options);
-
AddOverloadCandidates(initializers, context);
}
@@ -2546,7 +2560,6 @@ Expr* SemanticsVisitor::ResolveInvoke(InvokeExpr* expr)
context.loc = expr->loc;
context.sourceScope = m_outerScope;
context.baseExpr = GetBaseExpr(funcExpr);
-
// We run a special case here where an `InvokeExpr`
// with a single argument where the base/func expression names
// a type should always be treated as an explicit type coercion
@@ -2565,7 +2578,7 @@ Expr* SemanticsVisitor::ResolveInvoke(InvokeExpr* expr)
// type coercion.
bool typeOverloadChecked = false;
- if (expr->arguments.getCount() == 1)
+ if (expr->arguments.getCount() == 1 && !as<ExplicitCtorInvokeExpr>(expr))
{
if (const auto typeType = as<TypeType>(funcExpr->type))
{
diff --git a/source/slang/slang-ir-check-differentiability.cpp b/source/slang/slang-ir-check-differentiability.cpp
index d18c47689..9001295e0 100644
--- a/source/slang/slang-ir-check-differentiability.cpp
+++ b/source/slang/slang-ir-check-differentiability.cpp
@@ -249,6 +249,11 @@ public:
if (outerFuncInst->findDecoration<IRTorchEntryPointDecoration>())
return;
+ bool isSynthesizeConstructor = false;
+
+ if (auto constructor = funcInst->findDecoration<IRConstructorDecorartion>())
+ isSynthesizeConstructor = constructor->getSynthesizedStatus();
+
// This is a kernel function, we don't allow using TorchTensor type here.
for (auto b : funcInst->getBlocks())
{
@@ -256,6 +261,13 @@ public:
{
if (!checkType(inst->getDataType()))
{
+ if (isSynthesizeConstructor)
+ {
+ IRBuilder irBuilder(funcInst);
+ irBuilder.addDecoration(funcInst, kIROp_CudaHostDecoration);
+ return;
+ }
+
auto loc = inst->sourceLoc;
if (!loc.isValid())
loc = funcInst->sourceLoc;
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 7f399c366..a966189a0 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -10330,7 +10330,10 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
}
// Used for diagnostics
- getBuilder()->addConstructorDecoration(irFunc, constructorDecl->isSynthesized);
+ getBuilder()->addConstructorDecoration(
+ irFunc,
+ constructorDecl->containsFlavor(
+ ConstructorDecl::ConstructorFlavor::SynthesizedDefault));
}
// We lower whatever statement was stored on the declaration
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 2b4d64cd9..87c62f7ee 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -384,13 +384,6 @@ void initCommandOptions(CommandOptions& options)
"-restrictive-capability-check",
nullptr,
"Many capability warnings will become an error."},
- {OptionKind::ZeroInitialize,
- "-zero-initialize",
- nullptr,
- "Initialize all variables to zero."
- "Structs will set all struct-fields without an init expression to 0."
- "All variables will call their default constructor if not explicitly initialized as "
- "usual."},
{OptionKind::IgnoreCapabilities,
"-ignore-capabilities",
nullptr,
@@ -914,6 +907,13 @@ void initCommandOptions(CommandOptions& options)
"-parameter-blocks-use-register-spaces",
nullptr,
"Parameter blocks will use register spaces"},
+ {OptionKind::ZeroInitialize,
+ "-zero-initialize",
+ nullptr,
+ "Initialize all variables to zero."
+ "Structs will set all struct-fields without an init expression to 0."
+ "All variables will call their default constructor if not explicitly initialized as "
+ "usual."},
};
_addOptions(makeConstArrayView(deprecatedOpts), options);
@@ -2138,7 +2138,6 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
case OptionKind::VulkanUseEntryPointName:
case OptionKind::VulkanUseGLLayout:
case OptionKind::VulkanEmitReflection:
- case OptionKind::ZeroInitialize:
case OptionKind::IgnoreCapabilities:
case OptionKind::RestrictiveCapabilityCheck:
case OptionKind::MinimumSlangOptimization: