diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj index cbafa99752..13eea9f0a4 100644 --- a/build/visual-studio/slang/slang.vcxproj +++ b/build/visual-studio/slang/slang.vcxproj @@ -492,7 +492,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla - + @@ -737,7 +737,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla - + diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters index ab272f7e13..87a46477d6 100644 --- a/build/visual-studio/slang/slang.vcxproj.filters +++ b/build/visual-studio/slang/slang.vcxproj.filters @@ -564,7 +564,7 @@ Header Files - + Header Files @@ -1295,7 +1295,7 @@ Source Files - + Source Files diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 496fb7e328..7ebe77a8f2 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -598,7 +598,7 @@ DIAGNOSTIC(39999, Error, unableToFindSymbolInModule, "unable to find the mangled DIAGNOSTIC(39999, Error, overloadedParameterToHigherOrderFunction, "passing overloaded functions to higher order functions is not supported") -// 38xxx +// 38xxx DIAGNOSTIC(38000, Error, entryPointFunctionNotFound, "no function found matching entry point name '$0'") DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one function matches entry point name '$0'") @@ -735,9 +735,11 @@ DIAGNOSTIC(41010, Warning, missingReturn, "control flow may reach end of non-'vo DIAGNOSTIC(41011, Error, profileIncompatibleWithTargetSwitch, "__target_switch has no compatable target with current profile '$0'") DIAGNOSTIC(41012, Warning, profileImplicitlyUpgraded, "user set `profile` had an implicit upgrade applied to it, atoms added: '$0'") DIAGNOSTIC(41012, Error, profileImplicitlyUpgradedRestrictive, "user set `profile` had an implicit upgrade applied to it, atoms added: '$0'") -DIAGNOSTIC(41015, Error, usingUninitializedValue, "use of uninitialized value '$0'") -DIAGNOSTIC(41016, Warning, returningWithUninitializedOut, "returning without initializing out parameter '$0'") -DIAGNOSTIC(41017, Warning, returningWithPartiallyUninitializedOut, "returning without fully initializing out parameter '$0'") +DIAGNOSTIC(41015, Warning, usingUninitializedOut, "use of uninitialized out parameter '$0'") +DIAGNOSTIC(41016, Warning, usingUninitializedVariable, "use of uninitialized variable '$0'") +DIAGNOSTIC(41017, Warning, usingUninitializedGlobalVariable, "use of uninitialized global variable '$0'") +DIAGNOSTIC(41018, Warning, returningWithUninitializedOut, "returning without initializing out parameter '$0'") +DIAGNOSTIC(41019, Warning, returningWithPartiallyUninitializedOut, "returning without fully initializing out parameter '$0'") DIAGNOSTIC(41011, Error, typeDoesNotFitAnyValueSize, "type '$0' does not fit in the size required by its conforming interface.") DIAGNOSTIC(41012, Note, typeAndLimit, "sizeof($0) is $1, limit is $2") diff --git a/source/slang/slang-ir-use-uninitialized-out-param.cpp b/source/slang/slang-ir-use-uninitialized-out-param.cpp deleted file mode 100644 index 7e3ef9ca2d..0000000000 --- a/source/slang/slang-ir-use-uninitialized-out-param.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "slang-ir-use-uninitialized-out-param.h" -#include "slang-ir-util.h" -#include "slang-ir-reachability.h" - -namespace Slang -{ - class DiagnosticSink; - struct IRModule; - - struct StoreSite - { - IRInst* storeInst; - IRInst* address; - }; - - void checkForUsingUninitializedOutParams(IRFunc* func, DiagnosticSink* sink) - { - List outParams; - auto firstBlock = func->getFirstBlock(); - if (!firstBlock) - return; - - ReachabilityContext reachability(func); - - for (auto param : firstBlock->getParams()) - { - if (auto outType = as(param->getFullType())) - { - // Don't check `out Vertices` or `out Indices` parameters - // in mesh shaders. - // TODO: we should find a better way to represent these mesh shader - // parameters so they conform to the initialize before use convention. - // For example, we can use a `OutputVetices` and `OutputIndices` type - // to represent an output, like `OutputPatch` in domain shader. - // For now, we just skip the check for these parameters. - switch (outType->getValueType()->getOp()) - { - case kIROp_VerticesType: - case kIROp_IndicesType: - case kIROp_PrimitivesType: - continue; - default: - break; - } - } - else - { - continue; - } - List addresses; - addresses.add(param); - List stores; - // Collect all sub-addresses from the param. - for (Index i = 0; i < addresses.getCount(); i++) - { - auto addr = addresses[i]; - for (auto use = addr->firstUse; use; use = use->nextUse) - { - switch (use->getUser()->getOp()) - { - case kIROp_GetElementPtr: - case kIROp_FieldAddress: - addresses.add(use->getUser()); - break; - case kIROp_Store: - case kIROp_SwizzledStore: - // If we see a store of this address, add it to stores set. - if (use == use->getUser()->getOperands()) - stores.add(StoreSite{ use->getUser(), addr }); - break; - case kIROp_Call: - case kIROp_SPIRVAsm: - // If we see a call using this address, treat it as a store. - stores.add(StoreSite{ use->getUser(), addr }); - break; - case kIROp_SPIRVAsmOperandInst: - stores.add(StoreSite{ use->getUser()->getParent(), addr}); - break; - } - } - } - // Check all address loads. - List loadsAndReturns; - for (auto addr : addresses) - { - for (auto use = addr->firstUse; use; use = use->nextUse) - { - if (auto load = as(use->getUser())) - loadsAndReturns.add(load); - } - } - for(const auto& b : func->getBlocks()) - { - auto t = as(b->getTerminator()); - if (!t) continue; - loadsAndReturns.add(t); - } - - for (auto store : stores) - { - // Remove insts from `loads` that is reachable from the store. - for (Index i = 0; i < loadsAndReturns.getCount();) - { - auto load = as(loadsAndReturns[i]); - if (load && !canAddressesPotentiallyAlias(func, store.address, load->getPtr())) - continue; - if (reachability.isInstReachable(store.storeInst, loadsAndReturns[i])) - { - loadsAndReturns.fastRemoveAt(i); - } - else - { - i++; - } - } - } - // If there are any loads left, it means they are using uninitialized out params. - for (auto load : loadsAndReturns) - { - sink->diagnose( - load, - load->m_op == kIROp_Return - ? Diagnostics::returningWithUninitializedOut - : Diagnostics::usingUninitializedValue, - param); - } - } - } - - void checkForUsingUninitializedOutParams( - IRModule* module, - DiagnosticSink* sink) - { - for (auto inst : module->getGlobalInsts()) - { - if (auto func = as(inst)) - { - checkForUsingUninitializedOutParams(func, sink); - } - else if (auto generic = as(inst)) - { - auto retVal = findGenericReturnVal(generic); - if (auto funcVal = as(retVal)) - { - checkForUsingUninitializedOutParams(funcVal, sink); - } - } - } - } -} diff --git a/source/slang/slang-ir-use-uninitialized-values.cpp b/source/slang/slang-ir-use-uninitialized-values.cpp new file mode 100644 index 0000000000..762773ad4b --- /dev/null +++ b/source/slang/slang-ir-use-uninitialized-values.cpp @@ -0,0 +1,382 @@ +#include "slang-ir-use-uninitialized-values.h" +#include "slang-ir-insts.h" +#include "slang-ir-reachability.h" +#include "slang-ir.h" + +namespace Slang +{ + static bool isMetaOp(IRInst* inst) + { + switch (inst->getOp()) + { + // These instructions only look at the parameter's type, + // so passing an undefined value to them is permissible + case kIROp_IsBool: + case kIROp_IsInt: + case kIROp_IsUnsignedInt: + case kIROp_IsSignedInt: + case kIROp_IsHalf: + case kIROp_IsFloat: + case kIROp_IsVector: + case kIROp_GetNaturalStride: + case kIROp_TypeEquals: + return true; + default: + break; + } + + return false; + } + + // Casting to IRUndefined is currently vacuous + // (e.g. any IRInst can be cast to IRUndefined) + static bool isUndefinedValue(IRInst* inst) + { + return (inst->m_op == kIROp_undefined); + } + + static bool isUndefinedParam(IRParam* param) + { + auto outType = as(param->getFullType()); + if (!outType) + return false; + + // Don't check `out Vertices` or `out Indices` parameters + // in mesh shaders. + // TODO: we should find a better way to represent these mesh shader + // parameters so they conform to the initialize before use convention. + // For example, we can use a `OutputVetices` and `OutputIndices` type + // to represent an output, like `OutputPatch` in domain shader. + // For now, we just skip the check for these parameters. + switch (outType->getValueType()->getOp()) + { + case kIROp_VerticesType: + case kIROp_IndicesType: + case kIROp_PrimitivesType: + return false; + default: + break; + } + + return true; + } + + static bool isAliasable(IRInst* inst) + { + switch (inst->getOp()) + { + // These instructions generate (implicit) references to inst + case kIROp_FieldExtract: + case kIROp_FieldAddress: + case kIROp_GetElement: + case kIROp_GetElementPtr: + return true; + default: + break; + } + + return false; + } + + static bool isDifferentiableFunc(IRInst* func) + { + for (auto decor = func->getFirstDecoration(); decor; decor = decor->getNextDecoration()) + { + switch (decor->getOp()) + { + case kIROp_ForwardDerivativeDecoration: + case kIROp_ForwardDifferentiableDecoration: + case kIROp_BackwardDerivativeDecoration: + case kIROp_BackwardDifferentiableDecoration: + case kIROp_UserDefinedBackwardDerivativeDecoration: + return true; + default: + break; + } + } + + return false; + } + + static bool canIgnoreType(IRType* type) + { + if (as(type)) + return true; + + // For structs, ignore if its empty + if (as(type)) + return (type->getFirstChild() == nullptr); + + // Nothing to initialize for a pure interface + if (as(type)) + return true; + + // For pointers, check the value type (primarily for globals) + if (auto ptr = as(type)) + return canIgnoreType(ptr->getValueType()); + + // In the case of specializations, check returned type + if (auto spec = as(type)) + { + IRInst* base = spec->getBase(); + IRGeneric* generic = as(base); + IRInst* inner = findInnerMostGenericReturnVal(generic); + IRType* innerType = as(inner); + return canIgnoreType(innerType); + } + + return false; + } + + static List getAliasableInstructions(IRInst* inst) + { + List addresses; + + addresses.add(inst); + for (auto use = inst->firstUse; use; use = use->nextUse) + { + IRInst* user = use->getUser(); + + // Meta instructions only use the argument type + if (isMetaOp(user) || !isAliasable(user)) + continue; + + addresses.addRange(getAliasableInstructions(user)); + } + + return addresses; + } + + static void collectLoadStore(List& stores, List& loads, IRInst* user) + { + // Meta intrinsics (which evaluate on type) do nothing + if (isMetaOp(user)) + return; + + // Ignore instructions generating more aliases + if (isAliasable(user)) + return; + + switch (user->getOp()) + { + case kIROp_loop: + case kIROp_unconditionalBranch: + // TODO: Ignore branches for now + return; + + // These instructions will store data... + case kIROp_Store: + case kIROp_SwizzledStore: + // TODO: for calls, should make check that the + // function is passing as an out param + case kIROp_Call: + case kIROp_SPIRVAsm: + case kIROp_GenericAsm: + // For now assume that __intrinsic_asm blocks will do the right thing... + stores.add(user); + break; + + case kIROp_SPIRVAsmOperandInst: + // For SPIRV asm instructions, need to check out the entire + // block when doing reachability checks + stores.add(user->getParent()); + break; + + case kIROp_MakeExistential: + case kIROp_MakeExistentialWithRTTI: + // For specializing generic structs + stores.add(user); + break; + + // ... and the rest will load/use them + default: + loads.add(user); + break; + } + } + + static void cancelLoads(ReachabilityContext &reachability, const List& stores, List& loads) + { + // Remove all loads which are reachable from stores + for (auto store : stores) + { + for (Index i = 0; i < loads.getCount(); ) + { + if (reachability.isInstReachable(store, loads[i])) + loads.fastRemoveAt(i); + else + i++; + } + } + } + + static List getUnresolvedParamLoads(ReachabilityContext &reachability, IRFunc* func, IRInst* inst) + { + // Collect all aliasable addresses + auto addresses = getAliasableInstructions(inst); + + // Partition instructions + List stores; + List loads; + + for (auto alias : addresses) + { + // TODO: Mark specific parts assigned to for partial initialization checks + for (auto use = alias->firstUse; use; use = use->nextUse) + { + IRInst* user = use->getUser(); + collectLoadStore(stores, loads, user); + } + } + + // Only for out params we shall add all returns + for (const auto& b : func->getBlocks()) + { + auto t = as(b->getTerminator()); + if (!t) + continue; + + loads.add(t); + } + + cancelLoads(reachability, stores, loads); + + return loads; + } + + static List getUnresolvedVariableLoads(ReachabilityContext &reachability, IRInst* inst) + { + auto addresses = getAliasableInstructions(inst); + + // Partition instructions + List stores; + List loads; + + for (auto alias : addresses) + { + for (auto use = alias->firstUse; use; use = use->nextUse) + { + IRInst* user = use->getUser(); + collectLoadStore(stores, loads, user); + } + } + + cancelLoads(reachability, stores, loads); + + return loads; + } + + static void checkUninitializedValues(IRFunc* func, DiagnosticSink* sink) + { + if (isDifferentiableFunc(func)) + return; + + auto firstBlock = func->getFirstBlock(); + if (!firstBlock) + return; + + ReachabilityContext reachability(func); + + // Check out parameters + for (auto param : firstBlock->getParams()) + { + if (!isUndefinedParam(param)) + continue; + + auto loads = getUnresolvedParamLoads(reachability, func, param); + for (auto load : loads) + { + sink->diagnose(load, + as (load) + ? Diagnostics::returningWithUninitializedOut + : Diagnostics::usingUninitializedOut, + param); + } + } + + // Check ordinary instructions + for (auto inst = firstBlock->getFirstInst(); inst; inst = inst->getNextInst()) + { + if (!isUndefinedValue(inst)) + continue; + + IRType* type = inst->getFullType(); + if (canIgnoreType(type)) + continue; + + auto loads = getUnresolvedVariableLoads(reachability, inst); + for (auto load : loads) + { + sink->diagnose(load, + Diagnostics::usingUninitializedVariable, + inst); + } + } + } + + static void checkUninitializedGlobals(IRGlobalVar* variable, DiagnosticSink* sink) + { + IRType* type = variable->getFullType(); + if (canIgnoreType(type)) + return; + + // Check for semantic decorations + // (e.g. globals like gl_GlobalInvocationID) + if (variable->findDecoration()) + return; + + // Check for initialization blocks + for (auto inst : variable->getChildren()) + { + if (as(inst)) + return; + } + + auto addresses = getAliasableInstructions(variable); + + List stores; + List loads; + + for (auto alias : addresses) + { + for (auto use = alias->firstUse; use; use = use->nextUse) + { + IRInst* user = use->getUser(); + collectLoadStore(stores, loads, user); + + // Disregard if there is at least one store, + // since we cannot tell what the control flow is + if (stores.getCount()) + return; + } + } + + for (auto load : loads) + { + sink->diagnose(load, + Diagnostics::usingUninitializedGlobalVariable, + variable); + } + } + + void checkForUsingUninitializedValues(IRModule* module, DiagnosticSink* sink) + { + for (auto inst : module->getGlobalInsts()) + { + if (auto func = as(inst)) + { + checkUninitializedValues(func, sink); + } + else if (auto generic = as(inst)) + { + auto retVal = findGenericReturnVal(generic); + if (auto funcVal = as(retVal)) + checkUninitializedValues(funcVal, sink); + } + else if (auto global = as(inst)) + { + checkUninitializedGlobals(global, sink); + } + } + } +} diff --git a/source/slang/slang-ir-use-uninitialized-out-param.h b/source/slang/slang-ir-use-uninitialized-values.h similarity index 80% rename from source/slang/slang-ir-use-uninitialized-out-param.h rename to source/slang/slang-ir-use-uninitialized-values.h index fd090c4f99..9b6867a3b5 100644 --- a/source/slang/slang-ir-use-uninitialized-out-param.h +++ b/source/slang/slang-ir-use-uninitialized-values.h @@ -6,7 +6,7 @@ namespace Slang class DiagnosticSink; struct IRModule; - void checkForUsingUninitializedOutParams( + void checkForUsingUninitializedValues( IRModule* module, DiagnosticSink* sink); } diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index c946072f95..6fa2ce67f8 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -33,7 +33,7 @@ #include "slang-ir-clone.h" #include "slang-ir-lower-error-handling.h" #include "slang-ir-obfuscate-loc.h" -#include "slang-ir-use-uninitialized-out-param.h" +#include "slang-ir-use-uninitialized-values.h" #include "slang-ir-peephole.h" #include "slang-mangle.h" #include "slang-type-layout.h" @@ -10925,8 +10925,8 @@ RefPtr generateIRForTranslationUnit( // call graph) based on constraints imposed by different instructions. propagateConstExpr(module, compileRequest->getSink()); - // Check for using uninitialized out parameters. - checkForUsingUninitializedOutParams(module, compileRequest->getSink()); + // Check for using uninitialized values + checkForUsingUninitializedValues(module, compileRequest->getSink()); // TODO: give error messages if any `undefined` or // instructions remain. diff --git a/tests/bugs/gh-4434.slang b/tests/bugs/gh-4434.slang index 9752260088..c9a48d4391 100644 --- a/tests/bugs/gh-4434.slang +++ b/tests/bugs/gh-4434.slang @@ -15,17 +15,19 @@ RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint tid : SV_GroupIndex) { - bool a, b, c; - c = and(a, b); - - bool1 i, j, k; - bool2 l, m, n; - bool3 o, p, q; - bool4 r, s, t; - k = and(i, j); - n = and(m, l); - q = and(o, p); - t = and(r, s); + bool K = (bool)outputBuffer[tid]; + + bool c = and(K, K); + + bool1 K1 = K; + bool2 K2 = K; + bool3 K3 = K; + bool4 K4 = K; + + bool1 k = and(K, K); + bool2 n = and(K, K); + bool3 q = and(K, K); + bool4 t = and(K, K); k = !and(k, false); n = !and(n, false); diff --git a/tests/bugs/gh-4441.slang b/tests/bugs/gh-4441.slang index 59d577c8d5..a2de7e00dc 100644 --- a/tests/bugs/gh-4441.slang +++ b/tests/bugs/gh-4441.slang @@ -15,17 +15,19 @@ RWStructuredBuffer outputBuffer; [numthreads(4, 1, 1)] void computeMain(uint tid : SV_GroupIndex) { - bool a, b, c; - c = or(a, b); - - bool1 i, j, k; - bool2 l, m, n; - bool3 o, p, q; - bool4 r, s, t; - k = or(i, j); - n = or(m, l); - q = or(o, p); - t = or(r, s); + bool K = (bool)outputBuffer[tid]; + + bool c = or(K, K); + + bool1 K1 = K; + bool2 K2 = K; + bool3 K3 = K; + bool4 K4 = K; + + bool1 k = or(K, K); + bool2 n = or(K, K); + bool3 q = or(K, K); + bool4 t = or(K, K); k = or(k, true); n = or(n, true); diff --git a/tests/diagnostics/ctor-ordinary-retval-legal.slang b/tests/diagnostics/ctor-ordinary-retval-legal.slang index f2d1765ffb..8117b4c802 100644 --- a/tests/diagnostics/ctor-ordinary-retval-legal.slang +++ b/tests/diagnostics/ctor-ordinary-retval-legal.slang @@ -11,7 +11,8 @@ struct S { - float v; + float v; + __init() { v = 0; } } struct S1a : S diff --git a/tests/diagnostics/uninitialized-out.slang b/tests/diagnostics/uninitialized-out.slang deleted file mode 100644 index d0d87449fb..0000000000 --- a/tests/diagnostics/uninitialized-out.slang +++ /dev/null @@ -1,57 +0,0 @@ -//DIAGNOSTIC_TEST:SIMPLE: - -float foo(out float3 v) -{ - // This should error as we haven't set v before we read from it - float r = v.x + 1.0; - // This should warn as we haven't set v before we return - return r; -} - -// This should warn as we return without x being initialized -float bar(out float x) -{ - return 0; -} - -// This should also warn pointing at the implicit return -void baz(out float x) {} - -void twoReturns(bool b, out float y) -{ - if(b) - { - // Should warn - return; - } - y = 0; - // Shouldn't warn - return; -} - -void twoOkReturns(bool b, out float y) -{ - if(b) - { - // Shouldn't warn - unused(y); - return; - } - y = 0; - // Shouldn't warn - return; -} - -// TODO: This should warn that n is potentially uninitialized -int ok(bool b, out int n) -{ - if(b) - n = 0; - return n; -} - -// TODO: This should warn that arr isn't fully initialized -void partial(out float arr[2]) -{ - arr[0] = 1; -} diff --git a/tests/diagnostics/uninitialized.slang b/tests/diagnostics/uninitialized.slang new file mode 100644 index 0000000000..4779f45c94 --- /dev/null +++ b/tests/diagnostics/uninitialized.slang @@ -0,0 +1,263 @@ +//TEST:SIMPLE(filecheck=CHK): -target spirv + +// TODO: +// * warn potentially uninitialized variables (control flow) +// * warn partially uninitialized variables (structs, arrays, etc.) +// * warn uninitialized fields in constructors + +/////////////////////////////////// +// Uninitialized local variables // +/////////////////////////////////// + +// Should not warn here (unconditionalBranch) +float3 unconditional(int mode) +{ + float f(float) { return 1; } + + float k0; + float k1; + + if (mode == 1) + { + k1 = 1; + k0 = 1; + + const float w = k1 * f(1); + k0 = 4.0f * k0 * w; + k1 = 2.0f * k1 * w; + } + + return k0 + k1; +} + +// Warn here for branches using the variables +int conditional() +{ + int k; + //CHK-DAG: warning 41016: use of uninitialized variable 'k' + return (k > 0); +} + +// Using unitialized values +int use_undefined_value(int k) +{ + int x; + x += k; + //CHK-DAG: warning 41016: use of uninitialized variable 'x' + return x; +} + +// Returning uninitialized values +__generic +T generic_undefined_return() +{ + T x; + //CHK-DAG: warning 41016: use of uninitialized variable 'x' + return x; +} + +// Array variables +float undefined_array() +{ + float array[2]; + //CHK-DAG: warning 41016: use of uninitialized variable 'array' + return array[0]; +} + +float filled_array(int mode) +{ + float array[2]; + array[0] = 1.0f; + return array[0]; +} + +// Structs and nested structs +struct Data +{ + float value; +}; + +struct NestedData +{ + Data data; +}; + +// No warnings here, even thought autodiff generates +// IR which frequently returns undefined values +struct DiffStruct : IDifferentiable +{ + Data data; + float x; +} + +// Same story here +[ForwardDifferentiable] +DiffStruct differentiable(float x) +{ + DiffStruct ds; + ds.x = x; + return ds; +} + +// Empty structures should not generate diagnostics +// for empty default constructors +struct EmptyStruct +{ + __init() {} +}; + +// No warnings for empty structs even without __init() +struct NonEmptyStruct +{ + int field; + + __init() + { + field = 1; + } +}; + +// No warnings even when __init() is not specified +struct NoDefault +{ + int f(int i) + { + return i; + } +}; + +// Constructing the above structs +int constructors() +{ + EmptyStruct empty; + NoDefault no_default; + return no_default.f(1); +} + +// Using struct fields and nested structs +float structs() +{ + Data inputData = Data(1.0); + + float undefVar; + Data undefData; + NestedData nestedData; + + float result = inputData.value; + + //CHK-DAG: warning 41016: use of uninitialized variable 'undefVar' + result += undefVar; + + //CHK-DAG: warning 41016: use of uninitialized variable 'undefData' + result += undefData.value; + + //CHK-DAG: warning 41016: use of uninitialized variable 'nestedData' + result += nestedData.data.value; + + return result; +} + +//////////////////////////////////// +// Uninitialized global variables // +//////////////////////////////////// + +// Using groupshared variables +groupshared float4 gsConstexpr = float4(1.0f); +groupshared float4 gsUndefined; + +// OK +float use_constexpr_initialized_gs() +{ + return gsConstexpr.x; +} + +float use_undefined_gs() +{ + //CHK-DAG: warning 41017: use of uninitialized global variable 'gsUndefined' + return gsUndefined.x; +} + +// Using static variables +static const float cexprInitialized = 1.0f; +static float writtenNever; +static float writtenLater; + +// OK +float use_initialized_static() +{ + return cexprInitialized; +} + +// Should detect this and treat it as a store +void write_to_later() +{ + writtenLater = 1.0f; +} + +float use_never_written() +{ + //CHK-DAG: warning 41017: use of uninitialized global variable 'writtenNever' + return writtenNever; +} + +// OK because of prior store +float use_later_writte() +{ + return writtenLater; +} + +////////////////////////////////// +// Uninitialized out parameters // +////////////////////////////////// + +// Using before assigning +float regular_undefined_use(out float3 v) +{ + //CHK-DAG: warning 41015: use of uninitialized out parameter 'v' + float r = v.x + 1.0; + + //CHK-DAG: warning 41018: returning without initializing out parameter 'v' + return r; +} + +// Returning before assigning +float returning_undefined_use(out float x) +{ + //CHK-DAG: warning 41018: returning without initializing out parameter 'x' + return 0; +} + +// Implicit, still returning before assigning +void implicit_undefined_use(out float x) +{ + //CHK-DAG: warning 41018: returning without initializing out parameter 'x' +} + +// Warn on potential return paths +void control_flow_undefined(bool b, out float y) +{ + if(b) + { + //CHK-DAG: warning 41018: returning without initializing out parameter 'y' + return; + } + y = 0; + return; +} + +// No warnings if all paths are fine +void control_flow_defined(bool b, out float y) +{ + if(b) + { + unused(y); + return; + } + y = 0; + return; +} + +//CHK-NOT: warning 41015 +//CHK-NOT: warning 41016 +//CHK-NOT: warning 41017 +//CHK-NOT: warning 41018