diff options
21 files changed, 860 insertions, 6 deletions
diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 4b3a6cc9f..938e7bfbe 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -1181,6 +1181,7 @@ namespace Slang /// "under-construction" and not being checked, then it's safe to /// consider all names we've inserted so far. This is used when /// checking to see if a keyword is shadowed. + IgnoreInheritance = 1 << 4, ///< Lookup only non inheritance children of a struct (including `extension`) }; inline LookupOptions operator&(LookupOptions a, LookupOptions b) { diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 4f8dd3dc5..d35502235 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -213,6 +213,7 @@ namespace Slang /// Validate that the target type of an extension `decl` is valid. void _validateExtensionDeclTargetType(ExtensionDecl* decl); + void _validateExtensionDeclMembers(ExtensionDecl* decl); void visitExtensionDecl(ExtensionDecl* decl); }; @@ -1816,6 +1817,85 @@ namespace Slang checkVisibility(varDecl); } + static ConstructorDecl* _createCtor(SemanticsDeclVisitorBase* visitor, ASTBuilder* m_astBuilder, AggTypeDecl* decl) + { + auto ctor = m_astBuilder->create<ConstructorDecl>(); + auto ctorName = visitor->getName("$init"); + ctor->ownedScope = m_astBuilder->create<Scope>(); + ctor->ownedScope->containerDecl = ctor; + ctor->ownedScope->parent = visitor->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)); + 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->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>(); + decl->addMember(ctor); + return ctor; + } + + static ConstructorDecl* _getDefaultCtor(StructDecl* structDecl) + { + for (auto ctor : structDecl->getMembersOfType<ConstructorDecl>()) + { + if (!ctor->body || ctor->members.getCount() != 0) + continue; + return ctor; + } + return nullptr; + } + + + static List<ConstructorDecl*> _getCtorList(ASTBuilder* m_astBuilder, SemanticsVisitor* visitor, StructDecl* structDecl, ConstructorDecl** defaultCtorOut) + { + List<ConstructorDecl*> ctorList; + + auto ctorLookupResult = lookUpMember( + m_astBuilder, + visitor, + visitor->getName("$init"), + DeclRefType::create(m_astBuilder, structDecl), + structDecl->ownedScope, + LookupMask::Function, + LookupOptions::IgnoreInheritance); + + if (!ctorLookupResult.isValid()) + return ctorList; + + auto lookupResultHandle = [&](LookupResultItem& item) + { + auto ctor = as<ConstructorDecl>(item.declRef.getDecl()); + if (!ctor || !ctor->body) + return; + ctorList.add(ctor); + if (ctor->members.getCount() != 0) + return; + *defaultCtorOut = ctor; + }; + if (ctorLookupResult.items.getCount() == 0) + { + lookupResultHandle(ctorLookupResult.item); + return ctorList; + } + + for (auto m : ctorLookupResult.items) + { + lookupResultHandle(m); + } + + return ctorList; + } + void SemanticsDeclHeaderVisitor::visitStructDecl(StructDecl* structDecl) { // As described above in `SemanticsDeclHeaderVisitor::checkVarDeclCommon`, @@ -7271,12 +7351,160 @@ namespace Slang } } + + static SeqStmt* _ensureCtorBodyIsSeqStmt(ASTBuilder* m_astBuilder, ConstructorDecl* decl) + { + // It is possible BlockStmt has a child with the type of + // `ExpressionStmt` if an existing constructor has only 1 + // expression. This would be a senario we need to + // put the `ExpressionStmt` inside a `SeqStmt`. + auto stmt = as<BlockStmt>(decl->body); + if (!as<SeqStmt>(stmt->body)) + { + auto tmpExpr = stmt->body; + auto seqStmt = m_astBuilder->create<SeqStmt>(); + seqStmt->stmts.add(tmpExpr); + stmt->body = seqStmt; + return seqStmt; + } + return as<SeqStmt>(stmt->body); + } + void SemanticsDeclBodyVisitor::visitAggTypeDecl(AggTypeDecl* aggTypeDecl) { if (aggTypeDecl->hasTag(TypeTag::Incomplete) && aggTypeDecl->hasModifier<HLSLExportModifier>()) { getSink()->diagnose(aggTypeDecl->loc, Diagnostics::cannotExportIncompleteType, aggTypeDecl); } + + auto structDecl = as<StructDecl>(aggTypeDecl); + if (!structDecl) + return; + + struct DeclAndCtorInfo + { + StructDecl* parent = nullptr; + ConstructorDecl* defaultCtor = nullptr; + List<ConstructorDecl*> ctorList; + DeclAndCtorInfo() + { + } + DeclAndCtorInfo(ASTBuilder* m_astBuilder, SemanticsVisitor* visitor, StructDecl* parent, const bool getOnlyDefault) + { + if (getOnlyDefault) + defaultCtor = _getDefaultCtor(parent); + else + ctorList = _getCtorList(m_astBuilder, visitor, parent, &defaultCtor); + } + }; + List<DeclAndCtorInfo> inheritanceDefaultCtorList{}; + for (auto inheritanceMember : structDecl->getMembersOfType<InheritanceDecl>()) + { + auto declRefType = as<DeclRefType>(inheritanceMember->base.type); + if (!declRefType) + continue; + auto structOfInheritance = as<StructDecl>(declRefType->getDeclRef().getDecl()); + if (!structOfInheritance) + continue; + inheritanceDefaultCtorList.add(DeclAndCtorInfo(m_astBuilder, this, structOfInheritance, true)); + } + DeclAndCtorInfo structDeclInfo = DeclAndCtorInfo(m_astBuilder, this, structDecl, false); + + Index insertOffset = 0; + Dictionary<Decl*, Expr*> cachedDeclToCheckedVar; + for (auto ctor : structDeclInfo.ctorList) + { + auto seqStmt = _ensureCtorBodyIsSeqStmt(m_astBuilder, ctor); + auto seqStmtChild = m_astBuilder->create<SeqStmt>(); + seqStmtChild->stmts.reserve(inheritanceDefaultCtorList.getCount()); + for (auto& declInfo : inheritanceDefaultCtorList) + { + if (!declInfo.defaultCtor) + continue; + + auto ctorToInvoke = m_astBuilder->create<VarExpr>(); + ctorToInvoke->declRef = declInfo.defaultCtor->getDefaultDeclRef(); + ctorToInvoke->name = declInfo.defaultCtor->getName(); + ctorToInvoke->loc = declInfo.defaultCtor->loc; + ctorToInvoke->type = structDeclInfo.defaultCtor->returnType.type; + + auto invoke = m_astBuilder->create<InvokeExpr>(); + invoke->functionExpr = ctorToInvoke; + + ThisExpr* thisExpr = m_astBuilder->create<ThisExpr>(); + thisExpr->scope = ctor->ownedScope; + thisExpr->type = ctor->returnType.type; + + auto assign = m_astBuilder->create<AssignExpr>(); + assign->left = coerce(CoercionSite::Initializer, declInfo.defaultCtor->returnType.type, thisExpr); + assign->right = invoke; + auto stmt = m_astBuilder->create<ExpressionStmt>(); + stmt->expression = assign; + stmt->loc = ctor->loc; + + seqStmtChild->stmts.add(stmt); + } + + if (seqStmtChild->stmts.getCount() == 0) + continue; + + seqStmt->stmts.insert(0, seqStmtChild); + insertOffset = 1; + } + + for (auto ctor : structDeclInfo.ctorList) + { + auto seqStmt = _ensureCtorBodyIsSeqStmt(m_astBuilder, ctor); + auto seqStmtChild = m_astBuilder->create<SeqStmt>(); + seqStmtChild->stmts.reserve(structDecl->members.getCount()); + for (auto& m : structDecl->members) + { + auto varDeclBase = as<VarDeclBase>(m); + if (!varDeclBase + || !varDeclBase->initExpr) + continue; + + VarExpr* memberVarExpr = m_astBuilder->create<VarExpr>(); + memberVarExpr->scope = ctor->ownedScope; + memberVarExpr->name = m->getName(); + + auto assign = m_astBuilder->create<AssignExpr>(); + assign->left = memberVarExpr; + assign->right = varDeclBase->initExpr; + assign->loc = m->loc; + + auto stmt = m_astBuilder->create<ExpressionStmt>(); + stmt->expression = assign; + stmt->loc = m->loc; + + Expr* checkedMemberVarExpr; + if (cachedDeclToCheckedVar.containsKey(m)) + checkedMemberVarExpr = cachedDeclToCheckedVar[m]; + else + { + checkedMemberVarExpr = CheckTerm(memberVarExpr); + cachedDeclToCheckedVar.add({ m, checkedMemberVarExpr }); + } + if (!checkedMemberVarExpr->type.isLeftValue) + continue; + + seqStmtChild->stmts.add(stmt); + } + if (seqStmtChild->stmts.getCount() == 0) + continue; + seqStmt->stmts.insert(insertOffset, seqStmtChild); + } + + if (structDeclInfo.defaultCtor) + { + auto seqStmt = as<SeqStmt>(as<BlockStmt>(structDeclInfo.defaultCtor->body)->body); + if (seqStmt && seqStmt->stmts.getCount() == 0) + { + structDecl->members.remove(structDeclInfo.defaultCtor); + structDecl->invalidateMemberDictionary(); + structDecl->buildMemberDictionary(); + } + } } void SemanticsDeclHeaderVisitor::cloneModifiers(Decl* dest, Decl* src) @@ -7555,15 +7783,28 @@ namespace Slang } } + void SemanticsDeclBasesVisitor::_validateExtensionDeclMembers(ExtensionDecl* decl) + { + for (auto m : decl->members) + { + auto ctor = as<ConstructorDecl>(m); + if (!ctor || !ctor->body || ctor->members.getCount() != 0) + continue; + getSink()->diagnose(m->loc, Diagnostics::invalidMemberTypeInExtension, m->astNodeType); + } + } + void SemanticsDeclBasesVisitor::visitExtensionDecl(ExtensionDecl* decl) { - // We check the target type expression, and then validate + // We check the target type expression and members, and then validate // that the type it names is one that it makes sense // to extend. // decl->targetType = CheckProperType(decl->targetType); _validateExtensionDeclTargetType(decl); + _validateExtensionDeclMembers(decl); + for( auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>() ) { ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl); @@ -9320,6 +9561,12 @@ namespace Slang 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); + 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 e88db59f8..29747b7d5 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -4172,6 +4172,16 @@ namespace Slang return CreateErrorExpr(expr); } + Expr* SemanticsExprVisitor::visitCastToSuperTypeExpr(CastToSuperTypeExpr* expr) + { + // CastToSuperType is effectively a struct field. + // As long as the type is not readonly tagged we + // can use CastToSuperType as an L-value + if(!expr->type.hasReadOnlyOnTarget) + expr->type.isLeftValue = true; + return expr; + } + Expr* SemanticsExprVisitor::visitReturnValExpr(ReturnValExpr* expr) { auto scope = expr->scope; diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index ff7ce6978..2f8565f7f 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -2629,7 +2629,6 @@ namespace Slang CASE(OverloadedExpr) CASE(OverloadedExpr2) CASE(AggTypeCtorExpr) - CASE(CastToSuperTypeExpr) CASE(ModifierCastExpr) CASE(LetExpr) CASE(ExtractExistentialValueExpr) @@ -2647,6 +2646,7 @@ namespace Slang Expr* visitThisExpr(ThisExpr* expr); Expr* visitThisTypeExpr(ThisTypeExpr* expr); + Expr* visitCastToSuperTypeExpr(CastToSuperTypeExpr* expr); Expr* visitReturnValExpr(ReturnValExpr* expr); Expr* visitAndTypeExpr(AndTypeExpr* expr); Expr* visitPointerTypeExpr(PointerTypeExpr* expr); diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp index 3831fed84..7a38b08c0 100644 --- a/source/slang/slang-check-overload.cpp +++ b/source/slang/slang-check-overload.cpp @@ -987,6 +987,8 @@ namespace Slang // directly (it is only visible through the requirement witness // information for inheritance declarations). // + auto leftDeclRefParent = left.declRef.getParent(); + auto rightDeclRefParent = right.declRef.getParent(); bool leftIsInterfaceRequirement = isInterfaceRequirement(left.declRef.getDecl()); bool rightIsInterfaceRequirement = isInterfaceRequirement(right.declRef.getDecl()); if(leftIsInterfaceRequirement != rightIsInterfaceRequirement) @@ -998,11 +1000,11 @@ namespace Slang if(leftIsModule != rightIsModule) return int(rightIsModule) - int(leftIsModule); - // If both are interface requirements, prefer to more derived interface. + // If both are interface requirements, prefer the more derived interface. if (leftIsInterfaceRequirement && rightIsInterfaceRequirement) { - auto leftType = DeclRefType::create(m_astBuilder, left.declRef.getParent()); - auto rightType = DeclRefType::create(m_astBuilder, right.declRef.getParent()); + auto leftType = DeclRefType::create(m_astBuilder, leftDeclRefParent); + auto rightType = DeclRefType::create(m_astBuilder, rightDeclRefParent); if (!leftType->equals(rightType)) { @@ -1013,6 +1015,27 @@ namespace Slang } } + // If both parents are the same we have ambiguity + if(left.declRef.getParent() == right.declRef.getParent()) + return 0; + + auto leftAggType = leftDeclRefParent.as<AggTypeDeclBase>(); + auto rightAggType = rightDeclRefParent.as<AggTypeDeclBase>(); + if (leftAggType && rightAggType) + { + auto leftType = DeclRefType::create(m_astBuilder, leftDeclRefParent); + auto rightType = DeclRefType::create(m_astBuilder, rightDeclRefParent); + + auto inheritanceInfo = getShared()->getInheritanceInfo(rightType); + for (auto facet : inheritanceInfo.facets) + if (facet.getImpl()->getDeclRef().equals(leftDeclRefParent)) + return 1; + inheritanceInfo = getShared()->getInheritanceInfo(leftType); + for (auto facet : inheritanceInfo.facets) + if (facet.getImpl()->getDeclRef().equals(rightDeclRefParent)) + return -1; + } + // TODO: We should generalize above rules such that in a tie a declaration // A::m is better than B::m when all other factors are equal and // A inherits from B. diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 882a4c314..c2e76dffe 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -522,6 +522,7 @@ DIAGNOSTIC(30831, Error, cannotInheritFromImplicitlySealedDeclarationInAnotherMo DIAGNOSTIC(30832, Error, invalidTypeForInheritance, "type '$0' cannot be used for inheritance") DIAGNOSTIC(30850, Error, invalidExtensionOnType, "type '$0' cannot be extended. `extension` can only be used to extend a nominal type.") +DIAGNOSTIC(30851, Error, invalidMemberTypeInExtension, "$0 cannot be apart of an `extension`") // 309xx: subscripts DIAGNOSTIC(30900, Error, multiDimensionalArrayNotSupported, "multi-dimensional array is not supported.") diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp index 6a62303d2..2be9d29e8 100644 --- a/source/slang/slang-lookup.cpp +++ b/source/slang/slang-lookup.cpp @@ -416,6 +416,7 @@ static void _lookUpMembersInSuperTypeDeclImpl( // With semantics context, we can do a comprehensive lookup by scanning through // the linearized inheritance list. + auto selfType = DeclRefType::create(astBuilder, declRef); InheritanceInfo inheritanceInfo; if (auto extDeclRef = declRef.as<ExtensionDecl>()) { @@ -423,7 +424,6 @@ static void _lookUpMembersInSuperTypeDeclImpl( } else { - auto selfType = DeclRefType::create(astBuilder, declRef); selfType = selfType->getCanonicalType(); inheritanceInfo = semantics->getShared()->getInheritanceInfo(selfType); } @@ -442,6 +442,7 @@ static void _lookUpMembersInSuperTypeDeclImpl( continue; } + auto extensionFacet = as<ExtensionDecl>(facet.getImpl()->getDeclRef().getDecl()); // If we are looking up in an interface, and the lookup request told us // to skip interfaces, we should do so here. if (auto baseInterfaceDeclRef = containerDeclRef.as<InterfaceDecl>()) @@ -449,6 +450,14 @@ static void _lookUpMembersInSuperTypeDeclImpl( if (int(request.options) & int(LookupOptions::IgnoreBaseInterfaces)) continue; } + // If we are looking up only immediate members, ignore non "Self" facets or extension to "Self" + else if (int(request.options) & int(LookupOptions::IgnoreInheritance) + && (facet.getImpl()->directness != Facet::Directness::Self + && (!extensionFacet || !extensionFacet->targetType.type->equals(selfType)) + )) + { + continue; + } // Some things that are syntactically `InheritanceDecl`s don't actually // represent a subtype/supertype relationship, and thus we shouldn't diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 855bde6a6..52b3e64d4 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -4744,6 +4744,28 @@ struct ExprLoweringVisitorBase : public ExprVisitor<Derived, LoweredValInfo> // sense to specialize a key. return extractField(superType, value, declaredSubtypeWitness->getDeclRef().getDecl()); } + else if (auto transitiveSubtypeWitness = as<TransitiveSubtypeWitness>(subTypeWitness)) + { + // Try to resolve the inheritance situation which may show-up with 2+ levels of inheritance. + // We will recursivly follow through the subType->midType & midType->superType witnesses until + // we resolve DeclaredSubtypeWitness's + LoweredValInfo subToMid; + if (auto witness = as<SubtypeWitness>(transitiveSubtypeWitness->getSubToMid())) + subToMid = emitCastToConcreteSuperTypeRec(value, lowerType(context, witness->getSup()), witness); + else + { + SLANG_ASSERT(!"unhandled"); + return nullptr; + } + + if (auto witness = as<SubtypeWitness>(transitiveSubtypeWitness->getMidToSup())) + return emitCastToConcreteSuperTypeRec(subToMid, superType, witness); + else + { + SLANG_ASSERT(!"unhandled"); + return nullptr; + } + } else { SLANG_ASSERT(!"unhandled"); diff --git a/tests/language-feature/higher-order-functions/overload-different-param-count.slang b/tests/language-feature/higher-order-functions/overload-different-param-count.slang new file mode 100644 index 000000000..1af1b6706 --- /dev/null +++ b/tests/language-feature/higher-order-functions/overload-different-param-count.slang @@ -0,0 +1,59 @@ +//TEST:SIMPLE(filecheck=CHECK): -target glsl -entry computeMain -stage compute +// CHECK: main( +struct DiffMaterialData : IDifferentiable +{ + static const uint kMaterialParamCount = 20; + + // Material parameters. + float data[kMaterialParamCount]; + + [Differentiable] + __init() + { + [ForceUnroll] + for (uint i = 0; i < kMaterialParamCount; i++) + data[i] = 0.f; + } + + [Differentiable] + float read(inout uint offset) { return data[offset++]; } + + [Differentiable] + void read<let N : int>(out vector<float, N> value, inout uint offset) + { + [ForceUnroll] + for (uint i = 0; i < N; i++) + value[i] = read(offset); + } + + [Differentiable] + vector<float, N> read<let N : int>(inout uint offset) + { + vector<float, N> value; + this.read(value, offset); + return value; + } + + [mutating] + [Differentiable] + void write(float value, inout uint offset) { data[offset++] = value; } + + [mutating] + [Differentiable] + void write<let N : int>(vector<float, N> value, inout uint offset) + { + [ForceUnroll] + for (uint i = 0; i < N; i++) + this.write(value[i], offset); + } +}; + +RWStructuredBuffer<float3> outputBuffer; + +[numthreads(1,1,1)] +void computeMain() +{ + DiffMaterialData diffData; + uint offset = 0; + outputBuffer[0] = diffData.read<3>(offset); +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-extension-inheritance.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-extension-inheritance.slang new file mode 100644 index 000000000..4af5f2847 --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-extension-inheritance.slang @@ -0,0 +1,59 @@ +//TEST:SIMPLE(filecheck=CHECK): -target hlsl -stage compute -entry computeMain +RWStructuredBuffer<int> outputBuffer; + +//CHECK: error 30851 + +struct DefaultStructNoInit_base +{ + int data0 = 2; + int data1 = 2; +}; +extension DefaultStructNoInit_base +{ + __init(int indata) + { + //unused + } +} +struct DefaultStructNoInit : DefaultStructNoInit_base +{ + int dataInherit = 2; +}; + +struct DefaultStructWithInit_base +{ + int data0; + int data1 = 3; +}; +extension DefaultStructWithInit_base +{ + __init(int indata) + { + //unused + } + __init() + { + data0 = 3; + } +} +struct DefaultStructWithInit : DefaultStructWithInit_base +{ + int dataInherit = 3; +}; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructNoInit noInit = DefaultStructNoInit(); + DefaultStructWithInit withInit = DefaultStructWithInit(); + // BUF: 1 + outputBuffer[0] = true + && noInit.data0 == 2 + && noInit.data1 == 2 + && noInit.dataInherit == 2 + + && withInit.data0 == 3 + && withInit.data1 == 3 + && withInit.dataInherit == 3 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-extension.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-extension.slang new file mode 100644 index 000000000..1acfeeba7 --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-extension.slang @@ -0,0 +1,45 @@ +//TEST:SIMPLE(filecheck=CHECK): -target hlsl -stage compute -entry computeMain +RWStructuredBuffer<int> outputBuffer; + +//CHECK: error 30851 + +struct DefaultStructNoInit +{ + int data0 = 2; + int data1 = 2; + +}; +extension DefaultStructNoInit +{ +} + +struct DefaultStructWithInit +{ + int data0; + int data1 = 3; + int data2; +}; +extension DefaultStructWithInit +{ + __init() + { + data0 = 3; + data2 = 3; + } +} + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructNoInit noInit = DefaultStructNoInit(); + DefaultStructWithInit withInit = DefaultStructWithInit(); + // BUF: 1 + outputBuffer[0] = true + && noInit.data0 == 2 + && noInit.data1 == 2 + + && withInit.data0 == 3 + && withInit.data1 == 3 + && withInit.data2 == 3 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-import-target.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-import-target.slang new file mode 100644 index 000000000..23d1e3844 --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-import-target.slang @@ -0,0 +1,9 @@ +// tested import struct for struct-field-initializer-import.slang +struct DefaultStructNoInit +{ + int data0 = 2; +}; +struct DefaultStructNoInit2 +{ + int data0; +};
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-import.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-import.slang new file mode 100644 index 000000000..6c8b6fee1 --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-import.slang @@ -0,0 +1,26 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +import struct_field_initializer_import_target; +RWStructuredBuffer<int> outputBuffer; + +void modifyOut(out DefaultStructNoInit2 noInit2) +{ + noInit2.data0 = 2; +} + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructNoInit noInit; + DefaultStructNoInit2 noInit2; + modifyOut(noInit2); + // BUF: 1 + outputBuffer[0] = true + && noInit.data0 == 2 + && noInit2.data0 == 2 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-inherited-chain.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-inherited-chain.slang new file mode 100644 index 000000000..ef21a452f --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-inherited-chain.slang @@ -0,0 +1,53 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +static int myTwo = 2; +static int myThree = 1+2; + +struct DefaultStruct_bottom +{ + int data0 = 1; +}; +struct DefaultStruct_middle1 : DefaultStruct_bottom +{ + int data1 = 1; +}; +struct DefaultStruct_middle2 : DefaultStruct_middle1 +{ + int data2 = 1; +}; +struct DefaultStruct_top : DefaultStruct_middle2 +{ + int data3 = 1; +}; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStruct_bottom s1; + DefaultStruct_middle1 s2; + DefaultStruct_middle2 s3; + DefaultStruct_top s4; + + // BUF: 1 + outputBuffer[0] = true + && s1.data0 == 1 + + && s2.data0 == 1 + && s2.data1 == 1 + + && s3.data0 == 1 + && s3.data1 == 1 + && s3.data2 == 1 + + && s4.data0 == 1 + && s4.data1 == 1 + && s4.data2 == 1 + && s4.data3 == 1 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-inherited.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-inherited.slang new file mode 100644 index 000000000..8edb98447 --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-inherited.slang @@ -0,0 +1,66 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +static int myTwo = 2; +static int myThree = 1+2; + +struct DefaultStruct_base +{ + int data0 = 1; + int data1; + + __init() + { + data1 = 1; + } +}; +struct DefaultStruct1 : DefaultStruct_base +{ + int data2 = 1; +}; +struct DefaultStruct2 : DefaultStruct_base +{ + int data2 = 1; + __init() + { + if (data0 != 1) + { + data2 = 0; + } + } +}; +struct DefaultStruct3 : DefaultStruct_base +{ + __init() + { + } +}; +struct DefaultStruct4 : DefaultStruct_base +{ +}; +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStruct1 s1; + DefaultStruct2 s2; + DefaultStruct3 s3; + DefaultStruct4 s4; + // BUF: 1 + outputBuffer[0] = true + && s1.data0 == 1 + && s1.data1 == 1 + && s1.data2 == 1 + && s2.data0 == 1 + && s2.data1 == 1 + && s2.data2 == 1 + && s3.data0 == 1 + && s3.data1 == 1 + && s4.data0 == 1 + && s4.data1 == 1 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-init-extension-init-non-default.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-init-extension-init-non-default.slang new file mode 100644 index 000000000..2c6cfefac --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-init-extension-init-non-default.slang @@ -0,0 +1,32 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct DefaultStructWithInit +{ + int data0 = 3; + int data1 = 0; +}; +extension DefaultStructWithInit +{ + [mutating] + __init(int inData) + { + data1 = 3; + } +} + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructWithInit withInit = DefaultStructWithInit(5); + // BUF: 1 + outputBuffer[0] = true + && withInit.data0 == 3 + && withInit.data1 == 3 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-init-inheritance-write-to-same.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-init-inheritance-write-to-same.slang new file mode 100644 index 000000000..eff3595f1 --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-init-inheritance-write-to-same.slang @@ -0,0 +1,32 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct DefaultStructWithInit_base +{ + int data0 = 3; + int data1 = 0; +}; + +struct DefaultStructWithInit : DefaultStructWithInit_base +{ + __init() + { + data1 = 3; + } +}; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructWithInit withInit = DefaultStructWithInit(); + // BUF: 1 + outputBuffer[0] = true + && withInit.data0 == 3 + && withInit.data1 == 3 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-init-parameter.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-init-parameter.slang new file mode 100644 index 000000000..02773c9fa --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-init-parameter.slang @@ -0,0 +1,28 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct DefaultStructWithInit +{ + int data0 = 3; + int data1 = 0; + __init(int inData) + { + data1 = 3; + } +}; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructWithInit withInit = DefaultStructWithInit(5); + // BUF: 1 + outputBuffer[0] = true + && withInit.data0 == 3 + && withInit.data1 == 3 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang new file mode 100644 index 000000000..7f0b0577f --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang @@ -0,0 +1,32 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct DefaultStructNoInit_base +{ + static const int data0 = 2; + static int data1 = 2; + int data2 = 2; +}; + +struct DefaultStructNoInit : DefaultStructNoInit_base +{ + int data3 = 2; +}; + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructNoInit noInit; + // BUF: 1 + outputBuffer[0] = true + && noInit.data0 == 2 + && noInit.data1 == 2 + && noInit.data2 == 2 + && noInit.data3 == 2 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-initializer.slang b/tests/language-feature/struct-field-initializers/struct-field-initializer.slang new file mode 100644 index 000000000..0a03644aa --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-initializer.slang @@ -0,0 +1,60 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +static int myTwo = 2; +static int myThree = 1+2; + +struct DefaultStructNoInit +{ + int data0; + int data1 = myTwo; + int data2 = 2; +}; +struct DefaultStructWithInit +{ + int data0 = 3; + int data1 = myThree; + int data2; + __init() + { + data2 = 3; + } +}; +struct DefaultStructWithInit2 +{ + int data0 = 4; + int data1 = 1; + int data2 = 1; + __init() + { + data1 = 4; + data2 = 4; + } +}; +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultStructNoInit noInit = DefaultStructNoInit(); + noInit.data0 = 2; + DefaultStructWithInit withInit = DefaultStructWithInit(); + DefaultStructWithInit2 withInit2 = DefaultStructWithInit2(); + // BUF: 1 + outputBuffer[0] = true + && noInit.data0 == 2 + && noInit.data1 == 2 + && noInit.data2 == 2 + + && withInit.data0 == 3 + && withInit.data1 == 3 + && withInit.data2 == 3 + + && withInit2.data0 == 4 + && withInit2.data1 == 4 + && withInit2.data2 == 4 + ; +}
\ No newline at end of file diff --git a/tests/language-feature/struct-field-initializers/struct-field-no-initializer-complex-types.slang b/tests/language-feature/struct-field-initializers/struct-field-no-initializer-complex-types.slang new file mode 100644 index 000000000..a55c4725d --- /dev/null +++ b/tests/language-feature/struct-field-initializers/struct-field-no-initializer-complex-types.slang @@ -0,0 +1,40 @@ +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-vk -compute -entry computeMain -emit-spirv-directly +//TEST(compute, vulkan):COMPARE_COMPUTE(filecheck-buffer=BUF):-cpu -compute -entry computeMain +//TEST(smoke,compute):COMPARE_COMPUTE(filecheck-buffer=BUF):-dx12 -use-dxil -compute -entry computeMain + +//TEST_INPUT:ubuffer(data=[0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +struct DefaultData +{ + static const int2 val = int2(0, 1); + float2 size; + float scale; + float bias; +}; + +extension DefaultData +{ + int someGet() + { + return val.x; + } +} + +int loadDefaultData(inout DefaultData noInit) +{ + outputBuffer[1] = 1; + return noInit.someGet(); +} + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + DefaultData noInit; + outputBuffer[0] = true + && loadDefaultData(noInit) == 0; + ; + // BUF: 1 + // BUF-NEXT: 1 +}
\ No newline at end of file |
