Skip to content

Commit

Permalink
Keep implementing filling the ctor body
Browse files Browse the repository at this point in the history
  • Loading branch information
kaizhangNV committed Oct 8, 2024
1 parent 4f6cf2e commit 443609f
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 74 deletions.
1 change: 1 addition & 0 deletions docs/proposals/004-initialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ If the above code passes type check, then it will be used as the way to initiali
If the above code does not pass type check, and if there is only one constructor for`MyType` that is synthesized as described in the previous section (and therefore marked as `[Synthesized]`, Slang continues to check if `S` meets the standard of a "legacy C-style struct` type.
A type is a "legacy C-Style struct" if all of the following conditions are met:
- It is a user-defined struct type or a basic scalar, vector or matrix type, e.g. `int`, `float4x4`.
- It does not inherit from any other types.
- It does not contain any explicit constructors defined by the user.
- All its members have the same visibility as the type itself.
- All its members are legacy C-Style structs or arrays of legacy C-style structs.
Expand Down
9 changes: 7 additions & 2 deletions source/slang/slang-ast-decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ 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;
Dictionary<int, ConstructorDecl*> m_synthesizedCtorMap;
};

class ClassDecl : public AggTypeDecl
Expand Down Expand Up @@ -380,11 +385,11 @@ class ConstructorDecl : public FunctionDeclBase
Synthesized = 0x01,
// Member initialize constructor is definitely a synthesized ctor,
// but it takes parameters.
MemberInitCtor = 0x03
MemberInitCtor = 0x02
};

int m_tags = (int)ConstructorTags::None;
void addTag(ConstructorTags tag) { m_tags = (int)tag; }
void addTag(ConstructorTags tag) { m_tags |= (int)tag; }
bool containsTag(ConstructorTags tag) { return m_tags & (int)tag; }
};

Expand Down
36 changes: 27 additions & 9 deletions source/slang/slang-check-conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,12 @@ namespace Slang
return baseStructDeclRef;
}

// TODO: We might need to find a good way to get the synthesized constructor instead of traversing all of constructors.
ConstructorDecl* SemanticsVisitor::_getSynthesizedConstructor(StructDecl* structDecl, ConstructorDecl::ConstructorTags tags)
{
for (auto ctor : structDecl->getMembersOfType<ConstructorDecl>())
ConstructorDecl* synthesizedCtor = nullptr;
if(structDecl->m_synthesizedCtorMap.tryGetValue((int)tags, synthesizedCtor))
{
if (ctor->containsTag(tags))
{
return ctor;
}
return synthesizedCtor;
}

return nullptr;
Expand Down Expand Up @@ -276,12 +273,20 @@ namespace Slang
return true;
}

// TODO: We need to cache the result of this check, though it's not a heavy call.
bool SemanticsVisitor::isCStyleStruct(StructDecl* structDecl)
{
// Get the result from the cache first
if (bool *isCStyle = getShared()->isCStyleStruct(structDecl))
{
return *isCStyle;
}

// rules 1-4 are checked in _cStyleStructBasicCheck for all the non-array members
if(!_cStyleStructBasicCheck(structDecl))
{
getShared()->cacheCStyleStruct(structDecl, false);
return false;
}

// 5. All its members are legacy C-Style structs or arrays of legacy C-style structs
for (auto varDeclRef : getMembersOfType<VarDeclBase>(getASTBuilder(), structDecl, MemberFilterStyle::Instance))
Expand All @@ -300,7 +305,10 @@ namespace Slang
if(auto structDecl = _getStructDecl(elementType))
{
if (!_cStyleStructBasicCheck(structDecl))
{
getShared()->cacheCStyleStruct(structDecl, false);
return false;
}
}
else
{
Expand All @@ -309,6 +317,8 @@ namespace Slang
!as<MatrixExpressionType>(elementType) &&
!as<BasicExpressionType>(elementType))
{

getShared()->cacheCStyleStruct(structDecl, false);
return false;
}
}
Expand All @@ -317,9 +327,15 @@ namespace Slang
{
// all the other members still go through the basic check.
if (!_cStyleStructBasicCheck(varDecl))
{
getShared()->cacheCStyleStruct(structDecl, false);
return false;
}
}
}

// Cache the result
getShared()->cacheCStyleStruct(structDecl, true);
return true;
}

Expand Down Expand Up @@ -773,13 +789,15 @@ namespace Slang
!canCoerce(toType, fromInitializerListExpr->type, nullptr))
return _failedCoercion(toType, outToExpr, fromInitializerListExpr);

// try to invoke the user-defined constructor if it exists
// 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 (_invokeExprForExplicitCtor(toType, fromInitializerListExpr, outToExpr))
{
return true;
}

// try to invoke the synthesized constructor if it exists
// Try to invoke the synthesized constructor if it exists
if (_invokeExprForSynthesizedCtor(toType, fromInitializerListExpr, outToExpr))
{
return true;
Expand Down
150 changes: 87 additions & 63 deletions source/slang/slang-check-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ namespace Slang
}
DeclAndCtorInfo(ASTBuilder* m_astBuilder, SemanticsVisitor* visitor, StructDecl* parent, const bool getOnlyDefault)
{
this->parent = parent;
if (getOnlyDefault)
defaultCtor = _getDefaultCtor(parent);
else
Expand Down Expand Up @@ -6995,6 +6996,7 @@ namespace Slang
if (getDeclVisibility(varDecl) >= ctorVisibility)
{
resultMembers.add(varDecl);
structDecl->m_membersVisibleInCtor.add(varDecl);
}
}
};
Expand Down Expand Up @@ -7066,10 +7068,15 @@ namespace Slang
List<VarDeclBase*> resultMembers;
_searchMembersWithHigherVisibility(structDecl, ctorVisibility, resultMembers);

// If there is no members or none of members are visible, then don't bother to synthesize the member initialize constructor.
if (resultMembers.getCount() == 0)
return;

// synthesize the constructor signature:
// 1. The constructor's name is always `$init`, we create one without parameters now.
ConstructorDecl* ctor = _createCtor(this, getASTBuilder(), structDecl);
ctor->addTag(ConstructorDecl::ConstructorTags::MemberInitCtor);
structDecl->m_synthesizedCtorMap.addIfNotExists((int)ConstructorDecl::ConstructorTags::MemberInitCtor, ctor);

ctor->members.reserve(resultMembers.getCount());

Expand All @@ -7089,6 +7096,7 @@ namespace Slang

ctorParam->parentDecl = ctor;
ctorParam->nameAndLoc = NameLoc(member->getName(), ctor->loc);
ctorParam->loc = ctor->loc;
ctor->members.add(ctorParam);
}
ctor->members.reverse();
Expand Down Expand Up @@ -8690,34 +8698,75 @@ namespace Slang
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 = DeclRefType::create(getASTBuilder(), member->getDefaultDeclRef());

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,
bool isMemberInitCtor,
Index &paramIndex)
Index &ioParamIndex)
{
// e.g. this->base = BaseType();
// If the ctor is a synthesized default ctor, we need to initialize the base by using the default ctor of the base.
// If the ctor is a synthesized member init ctor, we need to initialize the base by using the memeber init ctor of the base.
for (auto& declInfo : inheritanceDefaultCtorList)
{
ConstructorDecl* baseCtor = nullptr;
List<Expr*> argumentList;

if (!isMemberInitCtor)
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(...);
if (baseCtor = _getSynthesizedConstructor(declInfo.parent, ConstructorDecl::ConstructorTags::MemberInitCtor))
{
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 (!declInfo.defaultCtor)
continue;
baseCtor = declInfo.defaultCtor;
}
else
{
// TODO: read parameters for the baseCtor
// 1. get baseCtor's parameters list
// 2. for loop on `createCtorParamExpr()` to get each parameter
// 3. fill the argument list for the baseCtor
(void) paramIndex;
continue;
}

auto ctorToInvoke = m_astBuilder->create<VarExpr>();
ctorToInvoke->declRef = baseCtor->getDefaultDeclRef();
Expand All @@ -8727,10 +8776,12 @@ namespace Slang

auto invoke = m_astBuilder->create<InvokeExpr>();
invoke->functionExpr = ctorToInvoke;
invoke->arguments.insertRange(0, argumentList);

auto assign = m_astBuilder->create<AssignExpr>();
assign->left = coerce(CoercionSite::Initializer, baseCtor->returnType.type, thisExpr);
assign->right = invoke;

auto stmt = m_astBuilder->create<ExpressionStmt>();
stmt->expression = assign;
stmt->loc = ctor->loc;
Expand All @@ -8739,44 +8790,6 @@ namespace Slang
}
}

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 = DeclRefType::create(getASTBuilder(), member->getDefaultDeclRef());

return memberExpr;
}

Expr* SemanticsDeclBodyVisitor::createCtorParamExpr(ConstructorDecl* ctor, Index paramIndex)
{
if (paramIndex < ctor->members.getCount())
{
if (auto param = as<ParamDecl>(ctor->members[paramIndex]))
{
if (param->initExpr)
{
return param->initExpr;
}
else
{
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::synthesizeCtorBodyForMember(
ConstructorDecl* ctor,
Decl* member,
Expand All @@ -8793,7 +8806,11 @@ namespace Slang
return;

Expr* initExpr = nullptr;
if (!isMemberInitCtor)
auto structDecl = as<StructDecl>(member->parentDecl);
bool useParamList = isMemberInitCtor;
useParamList = isMemberInitCtor && structDecl->m_membersVisibleInCtor.contains(varDeclBase);

if (!useParamList)
{
// For non-member initialization ctor (explicit ctor or synthesized default ctor),
// we can only initialize the member when it has an initExpr.
Expand All @@ -8803,12 +8820,18 @@ namespace Slang
}
else
{
// TODO: find the corresponding parameter, if we can't find it, there
// 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++);
// SLANG_RELEASE_ASSERT(initExpr);
return;
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);
Expand Down Expand Up @@ -8852,13 +8875,15 @@ namespace Slang
thisExpr->type = ctor->returnType.type;

bool isMemberInitCtor = ctor->containsTag(ConstructorDecl::ConstructorTags::MemberInitCtor);

// 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;

// Initialize base type by using its default constructor if it has one.
// The first step is to synthesize the initialization of the base member.
synthesizeCtorBodyForBases(ctor, inheritanceDefaultCtorList, thisExpr, seqStmtChild, isMemberInitCtor, ioParamIndex);

// Initialize member variables by using their default value if they have one
// e.g. this->member = default_value
// Then synthesize the initialization of the other members.
for (auto& m : structDecl->members)
{
synthesizeCtorBodyForMember(ctor, m, thisExpr, cachedDeclToCheckedVar, seqStmtChild, isMemberInitCtor, ioParamIndex);
Expand Down Expand Up @@ -11363,11 +11388,10 @@ namespace Slang
// to avoid circular checking logic
auto defaultCtor = _getDefaultCtor(structDecl);
if (!defaultCtor)
_createCtor(this, m_astBuilder, structDecl);


// ConstructorDecl* defaultCtorDecl = nullptr;
// _getCtorList(getASTBuilder(), this, structDecl, nullptr);
{
auto ctor = _createCtor(this, m_astBuilder, structDecl);
structDecl->m_synthesizedCtorMap.addIfNotExists((int)ConstructorDecl::ConstructorTags::Synthesized, ctor);
}

int backingWidth = 0;
[[maybe_unused]]
Expand Down
Loading

0 comments on commit 443609f

Please sign in to comment.