summaryrefslogtreecommitdiff
path: root/source/slang/slang-check-conversion.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-check-conversion.cpp')
-rw-r--r--source/slang/slang-check-conversion.cpp243
1 files changed, 243 insertions, 0 deletions
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,