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/core.meta.slang | 39 ++++--- source/slang/slang-ast-builder.cpp | 10 ++ source/slang/slang-ast-builder.h | 3 + source/slang/slang-ast-expr.h | 5 + source/slang/slang-ast-support-types.h | 3 + source/slang/slang-ast-type.h | 5 + source/slang/slang-check-conformance.cpp | 30 ++++-- source/slang/slang-check-constraint.cpp | 6 +- source/slang/slang-check-decl.cpp | 175 +++++++++++++++++++++++-------- source/slang/slang-check-expr.cpp | 10 +- source/slang/slang-check-impl.h | 21 +++- source/slang/slang-check-inheritance.cpp | 4 +- source/slang/slang-check-overload.cpp | 6 +- source/slang/slang-check-shader.cpp | 10 +- source/slang/slang-lower-to-ir.cpp | 5 + source/slang/slang-options.cpp | 5 + source/slang/slang.cpp | 2 +- 17 files changed, 251 insertions(+), 88 deletions(-) (limited to 'source') diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index fcb08984b..3f396b864 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -74,6 +74,13 @@ syntax snorm : SNormModifier; /// syntax __extern_cpp : ExternCppModifier; +__magic_type(DefaultInitializableType) +interface IDefaultInitializableType +{ + __builtin_requirement($( (int)BuiltinRequirementKind::DefaultInitializableConstructor)) + __init(); +} + interface IComparable { __builtin_requirement($( (int)BuiltinRequirementKind::Equals)) @@ -187,7 +194,9 @@ interface IFloat : IArithmetic, IDifferentiable /// A type that can be used as an operand for builtins [sealed] [builtin] -interface __BuiltinType {} +interface __BuiltinType +{ +} /// A type that can be used for arithmetic operations [sealed] @@ -385,20 +394,6 @@ interface IArray } } -__generic -__magic_type(ArrayExpressionType) -struct Array : IArray -{ - [ForceInline] - int getCount() { return N; } - - __subscript(int index) -> T - { - __intrinsic_op($(kIROp_GetElement)) - get; - } -} - // The "comma operator" is effectively just a generic function that returns its second // argument. The left-to-right evaluation order guaranteed by Slang then ensures that // `left` is evaluated before `right`. @@ -1037,6 +1032,20 @@ extension int16_t : IRangedValue static const int16_t minValue = -32768; } +__generic +__magic_type(ArrayExpressionType) +struct Array : IArray +{ + [ForceInline] + int getCount() { return N; } + + __subscript(int index) -> T + { + __intrinsic_op($(kIROp_GetElement)) + get; + } +} + /// An `N` component vector with elements of type `T`. __generic __magic_type(VectorExpressionType) diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp index 0c30366e8..a672c1b7e 100644 --- a/source/slang/slang-ast-builder.cpp +++ b/source/slang/slang-ast-builder.cpp @@ -419,6 +419,16 @@ bool ASTBuilder::isDifferentiableInterfaceAvailable() return (m_sharedASTBuilder->tryFindMagicDecl("DifferentiableType") != nullptr); } +DeclRef ASTBuilder::getDefaultInitializableTypeInterfaceDecl() +{ + DeclRef declRef = DeclRef(getBuiltinDeclRef("DefaultInitializableType", nullptr)); + return declRef; +} +Type* ASTBuilder::getDefaultInitializableType() +{ + return DeclRefType::create(m_sharedASTBuilder->m_astBuilder, getDefaultInitializableTypeInterfaceDecl()); +} + MeshOutputType* ASTBuilder::getMeshOutputTypeFromModifier( HLSLMeshShaderOutputModifier* modifier, Type* elementType, diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h index 1c6637c31..029c24216 100644 --- a/source/slang/slang-ast-builder.h +++ b/source/slang/slang-ast-builder.h @@ -485,6 +485,9 @@ public: bool isDifferentiableInterfaceAvailable(); + DeclRef getDefaultInitializableTypeInterfaceDecl(); + Type* getDefaultInitializableType(); + MeshOutputType* getMeshOutputTypeFromModifier( HLSLMeshShaderOutputModifier* modifier, Type* elementType, diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h index cf873bf0d..9ed725e1a 100644 --- a/source/slang/slang-ast-expr.h +++ b/source/slang/slang-ast-expr.h @@ -40,6 +40,11 @@ class VarExpr : public DeclRefExpr SLANG_AST_CLASS(VarExpr) }; +class DefaultConstructExpr : public Expr +{ + SLANG_AST_CLASS(DefaultConstructExpr) +}; + // An expression that references an overloaded set of declarations // having the same name. class OverloadedExpr : public Expr diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 2d238d717..648baa820 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -466,6 +466,7 @@ namespace Slang /// functions, so it belongs in the last phase of checking. /// DefinitionChecked, + DefaultConstructorReadyForUse = DefinitionChecked, /// The capabilities required by the decl is infered and validated. /// @@ -1601,6 +1602,8 @@ namespace Slang /// The kind of a builtin interface requirement that can be automatically synthesized. enum class BuiltinRequirementKind { + DefaultInitializableConstructor, ///< The `IDefaultInitializable.__init()` method + DifferentialType, ///< The `IDifferentiable.Differential` associated type requirement DZeroFunc, ///< The `IDifferentiable.dzero` function requirement DAddFunc, ///< The `IDifferentiable.dadd` function requirement diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index 74fae139c..b007ad854 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -450,6 +450,11 @@ class DifferentiableType : public BuiltinType SLANG_AST_CLASS(DifferentiableType) }; +class DefaultInitializableType : public BuiltinType +{ + SLANG_AST_CLASS(DefaultInitializableType); +}; + // A vector type, e.g., `vector` class VectorExpressionType : public ArithmeticExpressionType { diff --git a/source/slang/slang-check-conformance.cpp b/source/slang/slang-check-conformance.cpp index 59547ef0a..0ff4bfed4 100644 --- a/source/slang/slang-check-conformance.cpp +++ b/source/slang/slang-check-conformance.cpp @@ -51,19 +51,26 @@ namespace Slang SubtypeWitness* SemanticsVisitor::isSubtype( Type* subType, - Type* superType) + Type* superType, + IsSubTypeOptions isSubTypeOptions + ) { SubtypeWitness* result = nullptr; if (getShared()->tryGetSubtypeWitnessFromCache(subType, superType, result)) return result; - result = checkAndConstructSubtypeWitness(subType, superType); + result = checkAndConstructSubtypeWitness(subType, superType, isSubTypeOptions); + + if(int(isSubTypeOptions) & int(IsSubTypeOptions::NotReadyForLookup)) + return result; + getShared()->cacheSubtypeWitness(subType, superType, result); return result; } SubtypeWitness* SemanticsVisitor::checkAndConstructSubtypeWitness( Type* subType, - Type* superType) + Type* superType, + IsSubTypeOptions isSubTypeOptions) { // TODO: The Slang codebase is currently being quite slippery by conflating // multiple concepts, all under the banner of a "subtype" test: @@ -105,11 +112,14 @@ namespace Slang // tangling convertibility into it. // First, make sure both sub type and super type decl are ready for lookup. - if (auto subDeclRefType = as(subType)) + if ( !(int(isSubTypeOptions) & int(IsSubTypeOptions::NotReadyForLookup)) ) { - ensureDecl(subDeclRefType->getDeclRef().getDecl(), DeclCheckState::ReadyForLookup); + if (auto subDeclRefType = as(subType)) + { + ensureDecl(subDeclRefType->getDeclRef().getDecl(), DeclCheckState::ReadyForLookup); + } } - if (auto superDeclRefType = as(subType)) + if (auto superDeclRefType = as(superType)) { ensureDecl(superDeclRefType->getDeclRef().getDecl(), DeclCheckState::ReadyForLookup); } @@ -189,10 +199,10 @@ namespace Slang // We therefore simply recursively test both `T <: L` // and `T <: R`. // - auto leftWitness = isSubtype(subType, conjunctionSuperType->getLeft()); + auto leftWitness = isSubtype(subType, conjunctionSuperType->getLeft(), IsSubTypeOptions::None); if (!leftWitness) return nullptr; // - auto rightWitness = isSubtype(subType, conjunctionSuperType->getRight()); + auto rightWitness = isSubtype(subType, conjunctionSuperType->getRight(), IsSubTypeOptions::None); if (!rightWitness) return nullptr; // If both of the sub-relationships hold, we can construct @@ -238,7 +248,7 @@ namespace Slang bool SemanticsVisitor::isTypeDifferentiable(Type* type) { - return isSubtype(type, m_astBuilder->getDiffInterfaceType()); + return isSubtype(type, m_astBuilder->getDiffInterfaceType(), IsSubTypeOptions::None); } bool SemanticsVisitor::doesTypeHaveTag(Type* type, TypeTag tag) @@ -319,7 +329,7 @@ namespace Slang Type* type, Type* interfaceType) { - return isSubtype(type, interfaceType); + return isSubtype(type, interfaceType, IsSubTypeOptions::None); } TypeEqualityWitness* SemanticsVisitor::createTypeEqualityWitness( diff --git a/source/slang/slang-check-constraint.cpp b/source/slang/slang-check-constraint.cpp index 6a9b0adfa..23f7354d9 100644 --- a/source/slang/slang-check-constraint.cpp +++ b/source/slang/slang-check-constraint.cpp @@ -80,7 +80,7 @@ namespace Slang Type* interfaceType) { // The most basic test here should be: does the type declare conformance to the trait. - if(isSubtype(type, interfaceType)) + if(isSubtype(type, interfaceType, IsSubTypeOptions::None)) return type; // Just because `type` doesn't conform to the given `interfaceDeclRef`, that @@ -120,7 +120,7 @@ namespace Slang continue; // We only want to consider types that implement the target interface. - if(!isSubtype(candidateType, interfaceType)) + if(!isSubtype(candidateType, interfaceType, IsSubTypeOptions::None)) continue; // We only want to consider types where we can implicitly convert from `type` @@ -484,7 +484,7 @@ namespace Slang } // Search for a witness that shows the constraint is satisfied. - auto subTypeWitness = isSubtype(sub, sup); + auto subTypeWitness = isSubtype(sub, sup, IsSubTypeOptions::None); if(subTypeWitness) { // We found a witness, so it will become an (implicit) argument. 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(); diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index f7ad5bdbf..d0b82c6bd 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -2733,7 +2733,7 @@ namespace Slang // Get a reference to the builtin 'IDifferentiable' interface auto differentiableInterface = getASTBuilder()->getDifferentiableInterfaceType(); - auto conformanceWitness = as(isSubtype(primalType, differentiableInterface)); + auto conformanceWitness = as(isSubtype(primalType, differentiableInterface, IsSubTypeOptions::None)); // Check if the provided type inherits from IDifferentiable. // If not, return the original type. if (conformanceWitness) @@ -3116,6 +3116,12 @@ namespace Slang return expr; } + Expr* SemanticsExprVisitor::visitDefaultConstructExpr(DefaultConstructExpr* expr) + { + return expr; + } + + static bool _isSizeOfType(Type* type) { if (!type) @@ -3337,7 +3343,7 @@ namespace Slang valueType = typeType->getType(); // If value is a subtype of `type`, then this expr is always true. - if(isSubtype(valueType, expr->typeExpr.type)) + if(isSubtype(valueType, expr->typeExpr.type, IsSubTypeOptions::None)) { // Instead of returning a BoolLiteralExpr, we use a field to indicate this scenario, // so that the language server can still see the original syntax tree. diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index d7a4827fb..232cb623c 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -19,6 +19,13 @@ namespace Slang return sink->diagnose(pos, info, args...); } + enum class IsSubTypeOptions + { + None = 0, + /// Type may not be finished 'DeclCheckState::ReadyForLookup` + NotReadyForLookup = 1 << 0, + }; + /// Should the given `decl` be treated as a static rather than instance declaration? bool isEffectivelyStatic( Decl* decl); @@ -2105,9 +2112,15 @@ namespace Slang /// SubtypeWitness* isSubtype( Type* subType, - Type* superType); + Type* superType, + IsSubTypeOptions isSubTypeOptions + ); - SubtypeWitness* checkAndConstructSubtypeWitness(Type* subType, Type* superType); + SubtypeWitness* checkAndConstructSubtypeWitness( + Type* subType, + Type* superType, + IsSubTypeOptions isSubTypeOptions + ); bool isValidGenericConstraintType(Type* type); @@ -2127,7 +2140,7 @@ namespace Slang Type* subType, Type* superType) { - return isSubtype(subType, superType); + return isSubtype(subType, superType, IsSubTypeOptions::None); } /// Check whether `type` conforms to `interfaceDeclRef`, @@ -2734,6 +2747,8 @@ namespace Slang Expr* visitGetArrayLengthExpr(GetArrayLengthExpr* expr); + Expr* visitDefaultConstructExpr(DefaultConstructExpr* expr); + Expr* visitSPIRVAsmExpr(SPIRVAsmExpr*); /// Perform semantic checking on a `modifier` that is being applied to the given `type` diff --git a/source/slang/slang-check-inheritance.cpp b/source/slang/slang-check-inheritance.cpp index f1dbaef5c..7320d0463 100644 --- a/source/slang/slang-check-inheritance.cpp +++ b/source/slang/slang-check-inheritance.cpp @@ -417,7 +417,7 @@ namespace Slang break; auto rightBaseType = rightBase->origin.type; - if (visitor.isSubtype(leftBaseType, rightBaseType)) + if (visitor.isSubtype(leftBaseType, rightBaseType, IsSubTypeOptions::None)) { // If a type earlier in the list of bases is a subtype of // one later in the list, then the ordering is consistent @@ -427,7 +427,7 @@ namespace Slang // // TODO: decide whether to diagnose this case. } - else if (visitor.isSubtype(rightBaseType, leftBaseType)) + else if (visitor.isSubtype(rightBaseType, leftBaseType, IsSubTypeOptions::None)) { // If a type later in the list is a subtype of a type earlier // in the list, then the declared list of bases is inconsistent diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp index 367c200cc..d37c6e469 100644 --- a/source/slang/slang-check-overload.cpp +++ b/source/slang/slang-check-overload.cpp @@ -711,7 +711,7 @@ namespace Slang { if(context.mode != OverloadResolveContext::Mode::JustTrying) { - subTypeWitness = isSubtype(sub, sup); + subTypeWitness = isSubtype(sub, sup, IsSubTypeOptions::None); getSink()->diagnose(context.loc, Diagnostics::typeArgumentDoesNotConformToInterface, sub, sup); } return false; @@ -1024,9 +1024,9 @@ namespace Slang if (!leftType->equals(rightType)) { - if (isSubtype(leftType, rightType)) + if (isSubtype(leftType, rightType, IsSubTypeOptions::None)) return -1; - if (isSubtype(rightType, leftType)) + if (isSubtype(rightType, leftType, IsSubTypeOptions::None)) return 1; } } diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp index 43f8b55e8..2ebc9d3a4 100644 --- a/source/slang/slang-check-shader.cpp +++ b/source/slang/slang-check-shader.cpp @@ -1079,7 +1079,7 @@ namespace Slang auto interfaceType = getSup(getLinkage()->getASTBuilder(), DeclRef(constraintDecl)); // Use our semantic-checking logic to search for a witness to the required conformance - auto witness = visitor.isSubtype(argType, interfaceType); + auto witness = visitor.isSubtype(argType, interfaceType, IsSubTypeOptions::None); if(!witness) { // If no witness was found, then we will be unable to satisfy @@ -1111,7 +1111,7 @@ namespace Slang argType = getLinkage()->getASTBuilder()->getErrorType(); } - auto witness = visitor.isSubtype(argType, interfaceType); + auto witness = visitor.isSubtype(argType, interfaceType, IsSubTypeOptions::None); if (!witness) { // If no witness was found, then we will be unable to satisfy @@ -1262,7 +1262,7 @@ namespace Slang } auto sup = getSup(astBuilder, constraintDeclRef); - auto subTypeWitness = visitor.isSubtype(sub, sup); + auto subTypeWitness = visitor.isSubtype(sub, sup, IsSubTypeOptions::None); if(subTypeWitness) { genericArgs.add(subTypeWitness); @@ -1302,7 +1302,7 @@ namespace Slang auto paramType = as(param.object); auto argType = as(specializationArg.val); - auto witness = visitor.isSubtype(argType, paramType); + auto witness = visitor.isSubtype(argType, paramType, IsSubTypeOptions::None); if (!witness) { // If no witness was found, then we will be unable to satisfy @@ -1460,7 +1460,7 @@ namespace Slang ExpandedSpecializationArg arg; arg.val = argType; - arg.witness = visitor.isSubtype(argType, paramType); + arg.witness = visitor.isSubtype(argType, paramType, IsSubTypeOptions::None); specializationArgs.add(arg); } diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index bfc4c444d..4a6701acc 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -4444,6 +4444,11 @@ struct ExprLoweringVisitorBase : public ExprVisitor return LoweredValInfo::simple(getBuilder()->emitDefaultConstruct(irType)); } + LoweredValInfo visitDefaultConstructExpr(DefaultConstructExpr* expr) + { + return LoweredValInfo::simple(getBuilder()->emitDefaultConstruct(lowerType(context, expr->type))); + } + LoweredValInfo getDefaultVal(DeclRef decl) { if(auto initExpr = decl.getDecl()->initExpr) diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 04d48c85f..bcf9256bb 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -289,6 +289,10 @@ void initCommandOptions(CommandOptions& options) { OptionKind::Language, "-lang", "-lang ", "Set the language for the following input files."}, { OptionKind::MatrixLayoutColumn, "-matrix-layout-column-major", nullptr, "Set the default matrix layout to column-major."}, { OptionKind::MatrixLayoutRow,"-matrix-layout-row-major", nullptr, "Set the default matrix layout to row-major."}, + { OptionKind::ZeroInitialize, "-zero-initialize", nullptr, + "Initialize all variables to zero." + "Structs will set all struct-fields without an init expression to 0." + "All variables will call their default constructor if not explicitly initialized as usual."}, { OptionKind::IgnoreCapabilities,"-ignore-capabilities", nullptr, "Do not warn or error if capabilities are violated"}, { OptionKind::MinimumSlangOptimization, "-minimum-slang-optimization", nullptr, "Perform minimum code optimization in Slang to favor compilation time."}, { OptionKind::DisableNonEssentialValidations, "-disable-non-essential-validations", nullptr, "Disable non-essential IR validations such as use of uninitialized variables."}, @@ -1693,6 +1697,7 @@ SlangResult OptionsParser::_parse( case OptionKind::VulkanUseEntryPointName: case OptionKind::VulkanUseGLLayout: case OptionKind::VulkanEmitReflection: + case OptionKind::ZeroInitialize: case OptionKind::IgnoreCapabilities: case OptionKind::MinimumSlangOptimization: case OptionKind::DisableNonEssentialValidations: diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index ede22ec4f..10dc8f57c 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1515,7 +1515,7 @@ SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createTypeConformanceComponentTy SharedSemanticsContext sharedSemanticsContext(this, nullptr, &sink); SemanticsVisitor visitor(&sharedSemanticsContext); auto witness = - visitor.isSubtype((Slang::Type*)type, (Slang::Type*)interfaceType); + visitor.isSubtype((Slang::Type*)type, (Slang::Type*)interfaceType, IsSubTypeOptions::None); if (auto subtypeWitness = as(witness)) { result = new TypeConformance(this, subtypeWitness, conformanceIdOverride, &sink); -- cgit v1.2.3