diff options
| author | ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> | 2024-06-12 12:46:24 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-12 09:46:24 -0700 |
| commit | b7e824347a5de25cc013af30e43bd405b8b5698f (patch) | |
| tree | 74f477e883add12978d1586c89814364d8a1f275 /source/slang/slang-check-decl.cpp | |
| parent | ccc26c2d22d471ae649bf16f37ed1cd6cfbddd1b (diff) | |
Add slangc flag to `-zero-initialize` all variables (#3987)
* Default (zero'd) values with `-zero-initialize` flag
Adds `-zero-initialize` flag to set values to a __default() expression if they are missing a initExpr.
* address review and ensure __default calls ctor + zero's fields.
1. We must keep zero-initialize in SemanticsDeclHeaderVisitor. This is done because else a ctor will be initialized before we can set struct fields to `__default`.
2. IRDefaultCtorDecoration was added to track default ctor's with parent struct.
3. ParentAggTypeModifier was added to track ChildOfStruct->IRType for sharing data such as with functions. This is required to ensure we associate a lowered function with a lowered struct type
* Removed decoration to track defaultCtor in favor of field.
This was done since decorations are checked for IR objects, storing auxillary info does not work here as a result if usable object.
* address some review comments
Since `IDefaultInitializable` is taking a considerabley larger amount of time than anticipated I am pushing some of the other fixes requested. I did not remove the "IRStruct storing a default Ctor" hack yet.
mostly renamed/adjusted tests to work as intended
added test to ensure we don't synthisize a junk `= 0` when not in `zero initialize` mode
removed member in favor of sharedContext+dictionary.
* a working but incorrect impl
* default init without any IR hacks (fully working aside from generic/containored-types)
* Finish zero init code
1. IDefaultInitializer interface was added. If conforming, your type may be zero-initialized. To Conform a `__init()` is required
2. `[OnlyAutoInitIfForced]` was added. This attribute states that a default initializer should only be implicitly called if forced by the compiler (`zero-initialize` for example). This allows types which implicitly/explicitly conform to IDefaultInitialize to have optional auto-init behavior (which is Slang's default for user structs) to be disabled.
* note about `[OnlyAutoInitIfForced]`. This is required for std-lib to not automatically resolve init-expressions for std-lib, but it has the added benifit of allowing user made structs/classes to control the default behavior of initializing
* fix ErrType assumption
* testing why dx12 fails local but passes CI
* push vector changes to generic test
* push syntax adjustment, still figuring out what is wrong with cuda.
* remove debug changes & adjust style
* fix field-init expressions with structs initializers
don't init a static in a ctor. This would be illegal code and wrong code (init list in lower-to-ir)
* minor adjustments temporarily while the rest of the issue is discussed
* fix
* implement IDefaultInitializable
* remove a unneeded whitespace change
* fix type checking error
should be checking if a valid type is `Type`, not `BasicExpressionType`
* needs to be DeclRefType, not Type
* fix langguage server error
* change findinheritance for correctness + cleanup
* remove return false
verified the issue was `findInheritance`
* push attempt at language server fix
* still trying to fix inheritance
* added extension support, remove redundant code
Did not address all review comments yet, want to see if CI also passes my changes
* undo a change which caused CI to fail
* change logic + DefaultConstructExpr
setup code to use defaultConstructExpr when possible to construct a default without overhead of invoke/related
also changed code so parent's defaultInitializable propegates to derived member
* 1. fix error in `isSubtype` 2. add flag to isSubtype
`subtypeInheritanceIsNotFullyResolved` was added since we may not be done the lookup stage but still require `isSubtype` checking to verify usage of inheritance while working with inheritance. In This case we will just skip `ensureLookup` and "caching" (since we don't have a cache invalidation system, nor need)
* fix bug in logic + add test to better catch the bug
* address comment + isSubTypeOption + wrapper type test,
* fix wrong code adjustment
I checked on the CI and realized I caused a failure, mistake was made not negating some code
* syntax, class naming capital
* remove stdlib default initialize changes, replace with `__default()` for init
* remove redundant code + fix defaultConstruct emitting
previously defaultConstruct emitting was crashing due to having generics unresolved. By not resolving the default construct immediately, everything works.
* remove a coment
* add test to ensure static variables dont `init` inside a struct's `__init`
* fix Ptr members breaking struct use
* address review and add -zero-initialize test
`-zero-initialize` test was added to be sure debug pointers are not broken with default init values
---------
Co-authored-by: Yong He <yonghe@outlook.com>
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 175 |
1 files changed, 131 insertions, 44 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index bf61a6c2e..f8f6d2dcb 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -1870,7 +1870,7 @@ namespace Slang DeclRefType::create(m_astBuilder, structDecl), structDecl->ownedScope, LookupMask::Function, - LookupOptions::IgnoreInheritance); + (LookupOptions)((Index)LookupOptions::IgnoreInheritance | (Index)LookupOptions::NoDeref)); if (!ctorLookupResult.isValid()) return ctorList; @@ -1948,8 +1948,46 @@ namespace Slang checkVisibility(classDecl); } + static Expr* constructDefaultInitExprForVar(SemanticsVisitor* visitor, VarDeclBase* varDecl) + { + if (!varDecl->type || !varDecl->type.type) + return nullptr; + + ConstructorDecl* defaultCtor = nullptr; + auto declRefType = as<DeclRefType>(varDecl->type.type); + if (declRefType) + { + if (auto structDecl = as<StructDecl>(declRefType->getDeclRef().getDecl())) + { + defaultCtor = _getDefaultCtor(structDecl); + } + } + + if (defaultCtor) + { + auto* invoke = visitor->getASTBuilder()->create<InvokeExpr>(); + auto member = visitor->getASTBuilder()->getMemberDeclRef(declRefType->getDeclRef(), defaultCtor); + invoke->functionExpr = visitor->ConstructDeclRefExpr(member, nullptr, defaultCtor->loc, nullptr); + return invoke; + } + else + { + auto* defaultCall = visitor->getASTBuilder()->create<DefaultConstructExpr>(); + defaultCall->type = QualType(varDecl->type); + return defaultCall; + } + } void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl) { + // if zero initialize is true, set everything to a default + if (getOptionSet().hasOption(CompilerOptionName::ZeroInitialize) + && !varDecl->initExpr + && as<VarDecl>(varDecl) + ) + { + varDecl->initExpr = constructDefaultInitExprForVar(this, varDecl); + } + if (auto initExpr = varDecl->initExpr) { // Disable the short-circuiting for static const variable init expression @@ -4120,8 +4158,9 @@ namespace Slang } } + bool isDefaultInitializableType = requiredMemberDeclRef.getParent() == getASTBuilder()->getDefaultInitializableTypeInterfaceDecl(); bool isInWrapperType = isWrapperTypeDecl(context->parentDecl); - if (!isInWrapperType) + if (!isInWrapperType && !isDefaultInitializableType) { return false; } @@ -4136,6 +4175,10 @@ namespace Slang auto ctorName = getName("$init"); ctorDecl->nameAndLoc.name = ctorName; ctorDecl->nameAndLoc.loc = ctorDecl->loc; + + auto seqStmt = m_astBuilder->create<SeqStmt>(); + ctorDecl->body = seqStmt; + ctorDecl->returnType.type = DeclRefType::create(m_astBuilder, makeDeclRef(context->parentDecl)); List<Expr*> synArgs; addRequiredParamsToSynthesizedDecl(requiredMemberDeclRef, ctorDecl, synArgs); @@ -4143,52 +4186,55 @@ namespace Slang ThisExpr* synThis = nullptr; addModifiersToSynthesizedDecl(context, requiredMemberDeclRef, ctorDecl, synThis); - auto seqStmt = m_astBuilder->create<SeqStmt>(); - ctorDecl->body = seqStmt; - ctorDecl->returnType.type = DeclRefType::create(m_astBuilder, makeDeclRef(context->parentDecl)); - SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl)); - bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType); - - for (auto member : context->parentDecl->members) + if (isInWrapperType) { - if (auto varDecl = as<VarDeclBase>(member)) + SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl)); + bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType); + + for (auto member : context->parentDecl->members) { - auto varExpr = m_astBuilder->create<VarExpr>(); - varExpr->scope = ctorDecl->ownedScope; - varExpr->name = varDecl->getName(); - auto checkedVarExpr = CheckTerm(varExpr); - if (!checkedVarExpr) - return false; - if (as<ErrorType>(checkedVarExpr->type.type)) - return false; - auto assign = m_astBuilder->create<AssignExpr>(); - assign->left = checkedVarExpr; - auto temp = m_astBuilder->create<InvokeExpr>(); - auto lookupResult = lookUpMember( - m_astBuilder, - this, - ctorName, - varDecl->type.type, - ctorDecl->ownedScope, - LookupMask::Function, - LookupOptions::IgnoreBaseInterfaces); - temp->functionExpr = createLookupResultExpr(ctorName, lookupResult, nullptr, context->parentDecl->loc, nullptr); - temp->arguments.addRange(synArgs); - auto resolvedVar = ResolveInvoke(temp); - if (!resolvedVar) - return false; - assign->right = resolvedVar; - assign->type = m_astBuilder->getVoidType(); - bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type); + if (auto varDecl = as<VarDeclBase>(member)) + { + auto varExpr = m_astBuilder->create<VarExpr>(); + varExpr->scope = ctorDecl->ownedScope; + varExpr->name = varDecl->getName(); + auto checkedVarExpr = CheckTerm(varExpr); + if (!checkedVarExpr) + return false; + if (as<ErrorType>(checkedVarExpr->type.type)) + return false; + auto assign = m_astBuilder->create<AssignExpr>(); + assign->left = checkedVarExpr; + auto temp = m_astBuilder->create<InvokeExpr>(); + auto lookupResult = lookUpMember( + m_astBuilder, + this, + ctorName, + varDecl->type.type, + ctorDecl->ownedScope, + LookupMask::Function, + LookupOptions::IgnoreBaseInterfaces); + temp->functionExpr = createLookupResultExpr(ctorName, lookupResult, nullptr, context->parentDecl->loc, nullptr); + temp->arguments.addRange(synArgs); + auto resolvedVar = ResolveInvoke(temp); + if (!resolvedVar) + return false; + assign->right = resolvedVar; + assign->type = m_astBuilder->getVoidType(); + bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, varDecl->type.type); - auto stmt = m_astBuilder->create<ExpressionStmt>(); - stmt->expression = assign; - seqStmt->stmts.add(stmt); - break; + auto stmt = m_astBuilder->create<ExpressionStmt>(); + stmt->expression = assign; + seqStmt->stmts.add(stmt); + break; + } } } - - _addMethodWitness(witnessTable, requiredMemberDeclRef, makeDeclRef(ctorDecl)); + if (isDefaultInitializableType) + context->parentDecl->addMember(ctorDecl); + else + _addMethodWitness(witnessTable, requiredMemberDeclRef, makeDeclRef(ctorDecl)); + return true; } @@ -6057,6 +6103,21 @@ namespace Slang continue; } + if (this->getOptionSet().getBoolOption(CompilerOptionName::ZeroInitialize) && !isFromStdLib(decl)) + { + // Force add IDefaultInitializableType to any struct missing (transitively) `IDefaultInitializableType`. + auto* defaultInitializableType = m_astBuilder->getDefaultInitializableType(); + if(!isSubtype(DeclRefType::create(m_astBuilder, decl), defaultInitializableType, IsSubTypeOptions::NotReadyForLookup)) + { + InheritanceDecl* conformanceDecl = m_astBuilder->create<InheritanceDecl>(); + conformanceDecl->parentDecl = decl; + conformanceDecl->loc = decl->loc; + conformanceDecl->base.type = defaultInitializableType; + conformanceDecl->nameAndLoc.name = getName("$inheritance"); + decl->members.add(conformanceDecl); + } + } + // TODO: At this point we have the `baseDeclRef` // and could use it to perform further validity checks, // and/or to build up a more refined representation of @@ -7491,6 +7552,14 @@ namespace Slang // expression. This would be a senario we need to // put the `ExpressionStmt` inside a `SeqStmt`. auto stmt = as<BlockStmt>(decl->body); + if (!stmt) + { + auto tmpExpr = decl->body; + auto blockStmt = m_astBuilder->create<BlockStmt>(); + blockStmt->body = tmpExpr; + decl->body = blockStmt; + stmt = blockStmt; + } if (!as<SeqStmt>(stmt->body)) { auto tmpExpr = stmt->body; @@ -7542,6 +7611,21 @@ namespace Slang } DeclAndCtorInfo structDeclInfo = DeclAndCtorInfo(m_astBuilder, this, structDecl, false); + // ensure all varDecl members are processed up to SemanticsBodyVisitor so we can be sure that if init expressions + // of members are to be synthisised, they are. + bool isDefaultInitializableType = isSubtype(DeclRefType::create(m_astBuilder, structDecl), m_astBuilder->getDefaultInitializableType(), IsSubTypeOptions::None); + for (auto m : structDecl->members) + { + auto varDeclBase = as<VarDeclBase>(m); + if (!varDeclBase) + continue; + ensureDecl(m->getDefaultDeclRef(), DeclCheckState::DefaultConstructorReadyForUse); + if (!isDefaultInitializableType + || varDeclBase->initExpr) + continue; + varDeclBase->initExpr = constructDefaultInitExprForVar(this, varDeclBase); + } + Index insertOffset = 0; Dictionary<Decl*, Expr*> cachedDeclToCheckedVar; for (auto ctor : structDeclInfo.ctorList) @@ -7596,8 +7680,11 @@ namespace Slang for (auto& m : structDecl->members) { auto varDeclBase = as<VarDeclBase>(m); + + // Static variables are initialized at start of runtime, not inside a constructor if (!varDeclBase - || !varDeclBase->initExpr) + || !varDeclBase->initExpr + || varDeclBase->hasModifier<HLSLStaticModifier>()) continue; MemberExpr* memberExpr = m_astBuilder->create<MemberExpr>(); |
