Skip to content

Commit

Permalink
Implement a fallback mechanism to the legacy init list
Browse files Browse the repository at this point in the history
After we create the constructor invoke expression, we need
to resolve this expression by ResolveInvoke(), and if it's failed
we need a mechanism to fallback to the legacy initialization list
logic.

This change use sub visitor with a temporary diagnose sink to call
the ResolveInvoke(), such that compile process won't be aborted if
the error happens.

The condition allowing the fall back is that the struct has to be
a C-style struct, described in:
https://github.com/shader-slang/slang/blob/master/docs/proposals/004-initialization.md
  • Loading branch information
kaizhangNV committed Oct 6, 2024
1 parent 1123fb3 commit 73a61d1
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 33 deletions.
174 changes: 141 additions & 33 deletions source/slang/slang-check-conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,33 +201,126 @@ namespace Slang
return baseStructDeclRef;
}

bool SemanticsVisitor::_hasExplicitConstructor(StructDecl* structDecl)
// 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)
{
bool result = false;
for (auto ctor : getMembersOfType<ConstructorDecl>(getASTBuilder(), structDecl, MemberFilterStyle::All))
{
// constructor that is not synthesized must be user defined.
if (ctor.getDecl()->findModifier<SynthesizedModifier>() == nullptr)
if (ctor.getDecl()->findModifier<SynthesizedModifier>() != nullptr)
{
result = true;
break;
return ctor.getDecl();
}
}
return result;

return nullptr;
}

// 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)
static StructDecl* _getStructDecl(Type* type)
{
for (auto ctor : getMembersOfType<ConstructorDecl>(getASTBuilder(), structDecl, MemberFilterStyle::All))
if( as<VectorExpressionType>(type) ||
as<MatrixExpressionType>(type) ||
as<ArithmeticExpressionType>(type) ||
as<BuiltinType>(type))
{
if (ctor.getDecl()->findModifier<SynthesizedModifier>() != nullptr)
return nullptr;
}

if(auto declRefType = as<DeclRefType>(type))
{
auto structDecl = as<StructDecl>(declRefType->getDeclRef());
return structDecl.getDecl();
}

return nullptr;
}

bool SemanticsVisitor::_cStyleStructBasicCheck(Decl* decl)
{
// 1. It has to be a user-defined struct type, or a basic scalar, vector or matrix type
if (isFromStdLib(decl))
return false;

if (auto varDecl = as<VarDecl>(decl))
{
auto type = varDecl->getType();
if( as<VectorExpressionType>(type) ||
as<MatrixExpressionType>(type) ||
as<BasicExpressionType>(type))
return true;
else
return false;
}

auto structDecl = as<StructDecl>(decl);
if (!structDecl)
return false;

// 2. It cannot have inheritance
if (structDecl->getMembersOfType<InheritanceDecl>().getCount() > 0)
return false;

// 3. It cannot have explicit constructor
if (_hasExplicitConstructor(structDecl))
return false;

// 4. All of its members have to have the same visibility as the struct itself.
DeclVisibility structVisibility = getDeclVisibility(structDecl);
for (auto varDeclRef : getMembersOfType<VarDeclBase>(getASTBuilder(), structDecl, MemberFilterStyle::Instance))
{
auto varDecl = varDeclRef.getDecl();
if (getDeclVisibility(varDecl) != structVisibility)
{
return ctor.getDecl();
return false;
}
}
return true;
}

return nullptr;
// TODO: We need to cache the result of this check, though it's not a heavy call.
bool SemanticsVisitor::isCStyleStruct(StructDecl* structDecl)
{
// rules 1-4 are checked in _cStyleStructBasicCheck for all the non-array members
if(!_cStyleStructBasicCheck(structDecl))
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))
{
auto varDecl = varDeclRef.getDecl();

// if the member is an array, check if the element is legacy C-style rule.
if (auto arrayType = as<ArrayExpressionType>(varDecl->getType()))
{
auto* elementType = arrayType->getElementType();
ArrayExpressionType* nextType = nullptr;
while(nextType = as<ArrayExpressionType>(elementType))
{
elementType = nextType->getElementType();
}
if(auto structDecl = _getStructDecl(elementType))
{
if (!_cStyleStructBasicCheck(structDecl))
return false;
}
else
{
// if the element is not a struct, it has to be a scalar, vector or matrix.
if( !as<VectorExpressionType>(elementType) &&
!as<MatrixExpressionType>(elementType) &&
!as<BasicExpressionType>(elementType))
{
return false;
}
}
}
else
{
// all the other members still go through the basic check.
if (!_cStyleStructBasicCheck(varDecl))
return false;
}
}
return true;
}

Expr* SemanticsVisitor::_prepareCtorInvokeExpr(Type* toType, const SourceLoc& loc, const List<Expr*>& coercedArgs)
Expand All @@ -240,9 +333,7 @@ namespace Slang
constructorExpr->functionExpr = varExpr;
constructorExpr->arguments.addRange(coercedArgs);
constructorExpr->loc = loc;
auto resolvedConstructorExpr = CheckExpr(constructorExpr);

return resolvedConstructorExpr;
return constructorExpr;
}

// translation from initializer list to constructor invocation if the struct has constructor.
Expand All @@ -262,6 +353,7 @@ namespace Slang
if (_hasExplicitConstructor(toStructDeclRef.getDecl()))
{
auto ctorInvokeExpr = _prepareCtorInvokeExpr(toType, fromInitializerListExpr->loc, fromInitializerListExpr->args);
ctorInvokeExpr = CheckTerm(ctorInvokeExpr);
if (outExpr && ctorInvokeExpr)
{
*outExpr = ctorInvokeExpr;
Expand Down Expand Up @@ -289,19 +381,25 @@ namespace Slang
}

if (!structDecl || isFromStdLib(structDecl))
return true;
return false;

bool isCStyle = isCStyleStruct(structDecl);
auto synthesizedConstructor = _getSynthesizedConstructor(structDecl);
SLANG_ASSERT(synthesizedConstructor);

List<Expr*> coercedArgs;
auto ctorInvokeExpr = _prepareCtorInvokeExpr(toType, fromInitializerListExpr->loc, fromInitializerListExpr->args);

DiagnosticSink tempSink(getSourceManager(), nullptr);
SemanticsVisitor subVisitor(withSink(&tempSink));
ctorInvokeExpr = subVisitor.CheckExpr(ctorInvokeExpr);

if (ctorInvokeExpr)
{
// The reason we need to check the coercion again is that the ResolveInvoke() could still find us
// the wrong constructor when there is inheritance. e.g.:
// struct A { int a; };// __init(int a) will be synthesized.
// struct B : A { int b; };// __init(int a, int b) will be synthesized.
// struct A { int a; }; // __init(int a) will be synthesized.
// struct B : A { int b; }; // __init(int a, int b) will be synthesized.
// B b = {1}; // This should report an error, but the ResolveInvoke() will find the A.__init(int a).
//
// This will not happen when both A and B have explicit constructors, because 'coerce()' will be called after 'ResolveInvoke()'.
Expand All @@ -310,18 +408,32 @@ namespace Slang
//
// TODO: should we improve the ResolveInvoke() to handle this case? Because A.__init(int a) should not be found at first place,
// Base class cannot construct a Derived class.
if (!canCoerce(toType, ctorInvokeExpr->type, ctorInvokeExpr, nullptr))
if (!tempSink.getErrorCount())
{
// TODO:
// 1. Create a more explainable error message.
// 2. We should not diagnose here, because we will need a fall back mechanism.
return false;
if (!canCoerce(toType, ctorInvokeExpr->type, ctorInvokeExpr, nullptr))
{
// TODO:
// 1. Create a more explainable error message.
// 2. We should not diagnose here, because we will need a fall back mechanism.
tempSink.diagnose(fromInitializerListExpr->loc, Diagnostics::typeMismatch, toType, ctorInvokeExpr->type);
return false;
}
else
{
if (outExpr)
*outExpr = ctorInvokeExpr;
return true;
}
}
else
{
if (outExpr)
*outExpr = ctorInvokeExpr;
return true;
if (isCStyle)
{
return false;
}
Slang::ComPtr<ISlangBlob> blob;
tempSink.getBlobIfNeeded(blob.writeRef());
getSink()->diagnoseRaw(Severity::Error, static_cast<char const*>(blob->getBufferPointer()));
}
}
return false;
Expand Down Expand Up @@ -680,13 +792,9 @@ namespace Slang
}

// try to invoke the synthesized constructor if it exists
if (!_invokeExprForSynthesizedCtor(toType, fromInitializerListExpr, outToExpr))
if (_invokeExprForSynthesizedCtor(toType, fromInitializerListExpr, outToExpr))
{
// TODO: check if it's C-style initializer list
// if it's not, return error.
// if it is, return, fall-back to legacy logic
getSink()->diagnoseRaw(Severity::Error, "Synthesized Constructor is not found\n");
return false;
return true;
}

// We will fall back to the legacy logic of initialize list.
Expand Down
19 changes: 19 additions & 0 deletions source/slang/slang-check-decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7306,6 +7306,21 @@ namespace Slang
getSink()->diagnose(loc, Diagnostics::invalidEnumTagType, type);
}

bool SemanticsVisitor::_hasExplicitConstructor(StructDecl* structDecl)
{
bool result = false;
for (auto ctor : getMembersOfType<ConstructorDecl>(getASTBuilder(), structDecl, MemberFilterStyle::All))
{
// constructor that is not synthesized must be user defined.
if (ctor.getDecl()->findModifier<SynthesizedModifier>() == nullptr)
{
result = true;
break;
}
}
return result;
}

void SemanticsDeclBasesVisitor::visitEnumDecl(EnumDecl* decl)
{
SLANG_OUTER_SCOPE_CONTEXT_DECL_RAII(this, decl);
Expand Down Expand Up @@ -11225,6 +11240,10 @@ namespace Slang
if (!defaultCtor)
_createCtor(this, m_astBuilder, structDecl);


// ConstructorDecl* defaultCtorDecl = nullptr;
// _getCtorList(getASTBuilder(), this, structDecl, nullptr);

int backingWidth = 0;
[[maybe_unused]]
int totalWidth = 0;
Expand Down
2 changes: 2 additions & 0 deletions source/slang/slang-check-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2763,6 +2763,8 @@ namespace Slang
Expr* _prepareCtorInvokeExpr(Type* toType, const SourceLoc& loc, const List<Expr*>& coercedArgs);
bool _hasExplicitConstructor(StructDecl* structDecl);
ConstructorDecl* _getSynthesizedConstructor(StructDecl* structDecl);
bool isCStyleStruct(StructDecl* structDecl);
bool _cStyleStructBasicCheck(Decl* decl);
};


Expand Down

0 comments on commit 73a61d1

Please sign in to comment.