diff options
| author | ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> | 2024-04-16 13:06:23 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-16 13:06:23 -0400 |
| commit | d5d39dda2ec02c1dd21ba34a89f74b27092efcdd (patch) | |
| tree | 6bb276a42083b5db80080b81c6e18389f1784643 /source | |
| parent | 3192f34f57abd3245995342a0a5971ebbbbd945c (diff) | |
Init expressions for struct fields support, #3738 (#3907)
* Init expressions for struct members
Following commit handles init expressions of struct's.
The general implementation follows C++ init expression rules for classes & inherited classes.
The logic was implemented after type resolution (`SemanticsDeclAttributesVisitor`):
1. Create a default constructor if missing.
2. Check all member variables (`this` and `super`) for if a member has an init expression, continue to *3* if found.
3. For each constructor, insert a member variable's init expression at the beginning of a constructor. This is to follow how C++ does construction of objects.
Some important notes about implementation:
* We must handle the scenario that there is inheritance. To handle the inheritance information processing `findLevelsOfInheritance` was created.
* If a user manually sets overload rank's of constructor expression's we have no way to assume new default constructor overload ranks.
* address feedback
- moved all scope bound variables into if statment initializers
- added indent
- changed logic for overloadRank to be centered around positive numbers rather than negative
* Inheritance fixes universally & for struct field init
1. reimplemented struct field logic
2. implemented inheritance through calling a "super->init()" inisde a constructor for each "this".
3. implemented support for multi level inheritance (4+) and accessing members without a crash.
* add a way to ignore Forward declared constructors.
* a test and fix for a falcor failiure
the following case was not handled: creating an default Ctor due to a non L-Value struct field. Having an empty Ctor causes a warning.
* remove texture/sampler from test since it will break glsl
* get inheritance info using existing lookup logic
modified Facet lookups to store relative depth rather than arbitrary ::Self or' ::Direct for inheritance (which was 'wong' since depth 2 is not Direct, but was considered a Direct inheritance)
* cleanup unused
* cleanup unused functions and whitespace
* fix compile warning
* clean up, reorder, addressed language server fail
changed logic to safeguard bad code --> no longer breaks language server if code is incomplete.
remove the "semi-ordering" logic because caused a crash (and this code does nothing functionally, just thought it would be nice to add if '0 cost').
Remove rank setting for constructors, in place use an addition to the overload system: "this" expressions have calling priority over "super" expressions.
* undo all inheritance depth checks & code added to the inheritance checking algorithm
Reorder default ctor creation and auto-generation of constructor body.
* Handle same struct types during overload resolution
Changed overload resolution logic to properly handle same struct types; added test to check for multi-param same type function overload.
* remove unused ast object
Used unused object in an incorrect way. This caused the compiler to not flag a warning.
* extension support for default constructors
specialization is not supported with default constructors yet.
* fix bugs
Fix bug in override/overload logic with type comparisons.
used wrong type for ctor list construction
Specialization has not been added yet
* disallow default ctor inside extension
* adjust comment, add new tests
* add explicit types to invoke, use faster default ctor lookup.
* adjust syntax & naming as recomended
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 249 | ||||
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 10 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-check-overload.cpp | 29 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-lookup.cpp | 11 | ||||
| -rw-r--r-- | source/slang/slang-lower-to-ir.cpp | 22 |
8 files changed, 319 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"); |
