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