From b7e824347a5de25cc013af30e43bd405b8b5698f Mon Sep 17 00:00:00 2001 From: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:46:24 -0400 Subject: 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 --- source/slang/slang-check-decl.cpp | 175 ++++++++++++++++++++++++++++---------- 1 file changed, 131 insertions(+), 44 deletions(-) (limited to 'source/slang/slang-check-decl.cpp') 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(varDecl->type.type); + if (declRefType) + { + if (auto structDecl = as(declRefType->getDeclRef().getDecl())) + { + defaultCtor = _getDefaultCtor(structDecl); + } + } + + if (defaultCtor) + { + auto* invoke = visitor->getASTBuilder()->create(); + 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(); + 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->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(); + ctorDecl->body = seqStmt; + ctorDecl->returnType.type = DeclRefType::create(m_astBuilder, makeDeclRef(context->parentDecl)); List synArgs; addRequiredParamsToSynthesizedDecl(requiredMemberDeclRef, ctorDecl, synArgs); @@ -4143,52 +4186,55 @@ namespace Slang ThisExpr* synThis = nullptr; addModifiersToSynthesizedDecl(context, requiredMemberDeclRef, ctorDecl, synThis); - auto seqStmt = m_astBuilder->create(); - 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(member)) + SemanticsDeclBodyVisitor bodyVisitor(withParentFunc(ctorDecl)); + bodyVisitor.maybeRegisterDifferentiableType(m_astBuilder, context->conformingType); + + for (auto member : context->parentDecl->members) { - auto varExpr = m_astBuilder->create(); - varExpr->scope = ctorDecl->ownedScope; - varExpr->name = varDecl->getName(); - auto checkedVarExpr = CheckTerm(varExpr); - if (!checkedVarExpr) - return false; - if (as(checkedVarExpr->type.type)) - return false; - auto assign = m_astBuilder->create(); - assign->left = checkedVarExpr; - auto temp = m_astBuilder->create(); - 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(member)) + { + auto varExpr = m_astBuilder->create(); + varExpr->scope = ctorDecl->ownedScope; + varExpr->name = varDecl->getName(); + auto checkedVarExpr = CheckTerm(varExpr); + if (!checkedVarExpr) + return false; + if (as(checkedVarExpr->type.type)) + return false; + auto assign = m_astBuilder->create(); + assign->left = checkedVarExpr; + auto temp = m_astBuilder->create(); + 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(); - stmt->expression = assign; - seqStmt->stmts.add(stmt); - break; + auto stmt = m_astBuilder->create(); + 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(); + 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(decl->body); + if (!stmt) + { + auto tmpExpr = decl->body; + auto blockStmt = m_astBuilder->create(); + blockStmt->body = tmpExpr; + decl->body = blockStmt; + stmt = blockStmt; + } if (!as(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(m); + if (!varDeclBase) + continue; + ensureDecl(m->getDefaultDeclRef(), DeclCheckState::DefaultConstructorReadyForUse); + if (!isDefaultInitializableType + || varDeclBase->initExpr) + continue; + varDeclBase->initExpr = constructDefaultInitExprForVar(this, varDeclBase); + } + Index insertOffset = 0; Dictionary cachedDeclToCheckedVar; for (auto ctor : structDeclInfo.ctorList) @@ -7596,8 +7680,11 @@ namespace Slang for (auto& m : structDecl->members) { auto varDeclBase = as(m); + + // Static variables are initialized at start of runtime, not inside a constructor if (!varDeclBase - || !varDeclBase->initExpr) + || !varDeclBase->initExpr + || varDeclBase->hasModifier()) continue; MemberExpr* memberExpr = m_astBuilder->create(); -- cgit v1.2.3