summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvenkataram-nv <vedavamadath@nvidia.com>2024-07-16 14:54:53 -0700
committerGitHub <noreply@github.com>2024-07-16 14:54:53 -0700
commit05547e25353dd797791c2937679468d529d832d5 (patch)
treebc53e95b7b9e69474b83da3e5947712665c4664a
parentb5174b473ffb41e92b4efc844f60d7239f3322a3 (diff)
Warnings function parameters (#4626)
* Handle out/inout functions with separate consideration * Fixing bug with passing aliasable instructions * Handle autodiff functions (fwd and rev) in warning system * Handling interface methods * Handling ref parameters like out/inout * Temporary fix to remaining bugs * Refactoring methods and tests * Recursive check for empty structs * Using default initializable interface in tests * Resolving CI fail
-rw-r--r--source/slang/slang-ir-use-uninitialized-values.cpp130
-rw-r--r--tests/autodiff/material2/MxWeights.slang6
-rw-r--r--tests/bugs/generic-param-cast.slang2
-rw-r--r--tests/bugs/specialize-existential-in-generic.slang2
-rw-r--r--tests/compute/assoctype-func-param.slang4
-rw-r--r--tests/compute/assoctype-nested-lookup.slang4
-rw-r--r--tests/compute/dynamic-dispatch-17.slang4
-rw-r--r--tests/compute/empty-struct2.slang6
-rw-r--r--tests/compute/generic-closer.slang4
-rw-r--r--tests/compute/generic-default-arg.slang4
-rw-r--r--tests/compute/transitive-interface.slang4
-rw-r--r--tests/cuda/cuda-array-layout.slang2
-rw-r--r--tests/diagnostics/interfaces/anyvalue-size-validation.slang4
-rw-r--r--tests/diagnostics/uninitialized-globals.slang48
-rw-r--r--tests/diagnostics/uninitialized-local-variables.slang (renamed from tests/diagnostics/uninitialized.slang)112
-rw-r--r--tests/diagnostics/uninitialized-out-parameters.slang51
-rw-r--r--tests/diagnostics/uninitialized-out.slang.expected20
-rw-r--r--tests/diagnostics/uninitialized-use-functions.slang113
-rw-r--r--tests/language-feature/types/is-on-type.slang4
19 files changed, 347 insertions, 177 deletions
diff --git a/source/slang/slang-ir-use-uninitialized-values.cpp b/source/slang/slang-ir-use-uninitialized-values.cpp
index 762773ad4..7015d7470 100644
--- a/source/slang/slang-ir-use-uninitialized-values.cpp
+++ b/source/slang/slang-ir-use-uninitialized-values.cpp
@@ -28,11 +28,13 @@ namespace Slang
return false;
}
- // Casting to IRUndefined is currently vacuous
- // (e.g. any IRInst can be cast to IRUndefined)
- static bool isUndefinedValue(IRInst* inst)
+ static bool isUninitializedValue(IRInst* inst)
{
- return (inst->m_op == kIROp_undefined);
+ // Also consider var since it does not
+ // automatically mean it will be initialized
+ // (at least not as the user may have intended)
+ return (inst->m_op == kIROp_undefined)
+ || (inst->m_op == kIROp_Var);
}
static bool isUndefinedParam(IRParam* param)
@@ -98,14 +100,36 @@ namespace Slang
return false;
}
- static bool canIgnoreType(IRType* type)
+ static IRInst* resolveSpecialization(IRSpecialize* spec)
{
+ IRInst* base = spec->getBase();
+ IRGeneric* generic = as<IRGeneric>(base);
+ return findInnerMostGenericReturnVal(generic);
+ }
+
+ // The `upper` field contains the struct that the type is
+ // is contained in. It is used to check for empty structs.
+ static bool canIgnoreType(IRType* type, IRType* upper)
+ {
+ // In case specialization returns a function instead
+ if (!type)
+ return true;
+
if (as<IRVoidType>(type))
return true;
// For structs, ignore if its empty
- if (as<IRStructType>(type))
- return (type->getFirstChild() == nullptr);
+ if (auto str = as<IRStructType>(type))
+ {
+ int count = 0;
+ for (auto field : str->getFields())
+ {
+ IRType* ftype = field->getFieldType();
+ count += !canIgnoreType(ftype, type);
+ }
+
+ return (count == 0);
+ }
// Nothing to initialize for a pure interface
if (as<IRInterfaceType>(type))
@@ -113,16 +137,19 @@ namespace Slang
// For pointers, check the value type (primarily for globals)
if (auto ptr = as<IRPtrType>(type))
- return canIgnoreType(ptr->getValueType());
+ {
+ // Avoid the recursive step if its a
+ // recursive structure like a linked list
+ IRType* ptype = ptr->getValueType();
+ return (ptype != upper) && canIgnoreType(ptype, upper);
+ }
// In the case of specializations, check returned type
if (auto spec = as<IRSpecialize>(type))
{
- IRInst* base = spec->getBase();
- IRGeneric* generic = as<IRGeneric>(base);
- IRInst* inner = findInnerMostGenericReturnVal(generic);
+ IRInst* inner = resolveSpecialization(spec);
IRType* innerType = as<IRType>(inner);
- return canIgnoreType(innerType);
+ return canIgnoreType(innerType, upper);
}
return false;
@@ -146,8 +173,54 @@ namespace Slang
return addresses;
}
+
+ static void checkCallUsage(List<IRInst*>& stores, List<IRInst*>& loads, IRCall* call, IRInst* inst)
+ {
+ IRInst* callee = call->getCallee();
+
+ // Resolve the actual function
+ IRFunc* ftn = nullptr;
+ IRFuncType* ftype = nullptr;
+ if (auto spec = as<IRSpecialize>(callee))
+ ftn = as<IRFunc>(resolveSpecialization(spec));
+ else if (auto fwd = as<IRForwardDifferentiate>(callee))
+ ftn = as<IRFunc>(fwd->getBaseFn());
+ else if (auto rev = as<IRBackwardDifferentiate>(callee))
+ ftn = as<IRFunc>(rev->getBaseFn());
+ else if (auto wit = as<IRLookupWitnessMethod>(callee))
+ ftype = as<IRFuncType>(callee->getFullType());
+ else
+ ftn = as<IRFunc>(callee);
+
+ // Find the argument index so we can fetch the type
+ int index = 0;
+
+ auto args = call->getArgsList();
+ for (int i = 0; i < args.getCount(); i++)
+ {
+ if (args[i] == inst)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ if (ftn)
+ ftype = as<IRFuncType>(ftn->getFullType());
+
+ if (!ftype)
+ return;
+
+ // Consider it as a store if its passed
+ // as an out/inout/ref parameter
+ IRType* type = ftype->getParamType(index);
+ if (as<IROutType>(type) || as<IRInOutType>(type) || as<IRRefType>(type))
+ stores.add(call);
+ else
+ loads.add(call);
+ }
- static void collectLoadStore(List<IRInst*>& stores, List<IRInst*>& loads, IRInst* user)
+ static void collectLoadStore(List<IRInst*>& stores, List<IRInst*>& loads, IRInst* user, IRInst* inst)
{
// Meta intrinsics (which evaluate on type) do nothing
if (isMetaOp(user))
@@ -163,13 +236,17 @@ namespace Slang
case kIROp_unconditionalBranch:
// TODO: Ignore branches for now
return;
+
+ case kIROp_Call:
+ // Function calls can be either
+ // stores or loads depending on
+ // whether the callee takes it
+ // in as a out parameter or not
+ return checkCallUsage(stores, loads, as<IRCall>(user), inst);
// 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...
@@ -187,6 +264,11 @@ namespace Slang
// For specializing generic structs
stores.add(user);
break;
+
+ // Miscellaenous cases
+ case kIROp_ManagedPtrAttach:
+ stores.add(user);
+ break;
// ... and the rest will load/use them
default:
@@ -225,7 +307,7 @@ namespace Slang
for (auto use = alias->firstUse; use; use = use->nextUse)
{
IRInst* user = use->getUser();
- collectLoadStore(stores, loads, user);
+ collectLoadStore(stores, loads, user, alias);
}
}
@@ -257,7 +339,7 @@ namespace Slang
for (auto use = alias->firstUse; use; use = use->nextUse)
{
IRInst* user = use->getUser();
- collectLoadStore(stores, loads, user);
+ collectLoadStore(stores, loads, user, alias);
}
}
@@ -297,11 +379,11 @@ namespace Slang
// Check ordinary instructions
for (auto inst = firstBlock->getFirstInst(); inst; inst = inst->getNextInst())
{
- if (!isUndefinedValue(inst))
+ if (!isUninitializedValue(inst))
continue;
IRType* type = inst->getFullType();
- if (canIgnoreType(type))
+ if (canIgnoreType(type, nullptr))
continue;
auto loads = getUnresolvedVariableLoads(reachability, inst);
@@ -317,7 +399,7 @@ namespace Slang
static void checkUninitializedGlobals(IRGlobalVar* variable, DiagnosticSink* sink)
{
IRType* type = variable->getFullType();
- if (canIgnoreType(type))
+ if (canIgnoreType(type, nullptr))
return;
// Check for semantic decorations
@@ -331,7 +413,7 @@ namespace Slang
if (as<IRBlock>(inst))
return;
}
-
+
auto addresses = getAliasableInstructions(variable);
List<IRInst*> stores;
@@ -342,12 +424,14 @@ namespace Slang
for (auto use = alias->firstUse; use; use = use->nextUse)
{
IRInst* user = use->getUser();
- collectLoadStore(stores, loads, user);
+ collectLoadStore(stores, loads, user, alias);
// Disregard if there is at least one store,
// since we cannot tell what the control flow is
if (stores.getCount())
return;
+
+ // TODO: see if we can do better here (another kind of reachability check?)
}
}
diff --git a/tests/autodiff/material2/MxWeights.slang b/tests/autodiff/material2/MxWeights.slang
index 1e9c7d36c..244d97c73 100644
--- a/tests/autodiff/material2/MxWeights.slang
+++ b/tests/autodiff/material2/MxWeights.slang
@@ -3,6 +3,12 @@
public struct MxWeights<let TBsdfCount : int>
{
public float3 weights[TBsdfCount];
+
+ public __init()
+ {
+ for (int i = 0; i < TBsdfCount; i++)
+ weights[i] = float3(0.0f);
+ }
}
public interface IMxLayeredMaterialData
diff --git a/tests/bugs/generic-param-cast.slang b/tests/bugs/generic-param-cast.slang
index 20b30a433..e2dbcc285 100644
--- a/tests/bugs/generic-param-cast.slang
+++ b/tests/bugs/generic-param-cast.slang
@@ -8,7 +8,7 @@ struct A<let I : int>
int f() { return I; }
};
-struct B<let U : uint>
+struct B<let U : uint> : IDefaultInitializable
{
A<U> a;
};
diff --git a/tests/bugs/specialize-existential-in-generic.slang b/tests/bugs/specialize-existential-in-generic.slang
index 695c7bc29..cb05aea14 100644
--- a/tests/bugs/specialize-existential-in-generic.slang
+++ b/tests/bugs/specialize-existential-in-generic.slang
@@ -18,7 +18,7 @@ struct Impl : IFoo
Assoc getValue() { Assoc r; return r; }
}
-struct GenType<T : IFoo>
+struct GenType<T : IFoo> : IDefaultInitializable
{
T obj;
int doThing()
diff --git a/tests/compute/assoctype-func-param.slang b/tests/compute/assoctype-func-param.slang
index 830cc00cc..6d6bf5b6d 100644
--- a/tests/compute/assoctype-func-param.slang
+++ b/tests/compute/assoctype-func-param.slang
@@ -37,7 +37,7 @@ struct GenStruct<T> : IBase
U.RetT test<U:IBase>(U.RetT val)
{
- U obj;
+ U obj = U();
U.SubTypeT sb = obj.setVal(val);
return obj.getVal(sb);
}
@@ -50,4 +50,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
/* wrong error message for the following line */
float outVal = test<GenStruct<float> >(inVal);
outputBuffer[tid] = outVal.x;
-} \ No newline at end of file
+}
diff --git a/tests/compute/assoctype-nested-lookup.slang b/tests/compute/assoctype-nested-lookup.slang
index b1ca75d49..64a5d29a5 100644
--- a/tests/compute/assoctype-nested-lookup.slang
+++ b/tests/compute/assoctype-nested-lookup.slang
@@ -12,7 +12,7 @@ interface IFoo : IDefaultInitializable
};
-struct FooPair<T : IFoo> : IFoo
+struct FooPair<T : IFoo> : IFoo, IDefaultInitializable
{
T a;
T.Bar b;
@@ -41,4 +41,4 @@ void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID)
{
FooPair<ConcreteFoo>.Bar pair;
test(pair);
-} \ No newline at end of file
+}
diff --git a/tests/compute/dynamic-dispatch-17.slang b/tests/compute/dynamic-dispatch-17.slang
index bc2b9a6d9..e89482a5b 100644
--- a/tests/compute/dynamic-dispatch-17.slang
+++ b/tests/compute/dynamic-dispatch-17.slang
@@ -52,7 +52,7 @@ struct FloatVal : IInterface
float val;
float run<Z:IReturnsZero>()
{
- Z z;
+ Z z = Z();
return val + z.get();
}
};
@@ -62,7 +62,7 @@ struct Float4Val : IInterface
Float4Struct val;
float run<Z:IReturnsZero>()
{
- Z z;
+ Z z = Z();
return val.val.x + val.val.y + z.get();
}
};
diff --git a/tests/compute/empty-struct2.slang b/tests/compute/empty-struct2.slang
index 27e587b42..303cfd234 100644
--- a/tests/compute/empty-struct2.slang
+++ b/tests/compute/empty-struct2.slang
@@ -25,8 +25,8 @@ struct EmptyS : IEmptyS
struct Empty<TT : IEmptyS> : IInterface
{
typedef TT T;
- TT value;
- float a;
+ TT value = TT();
+ float a = 0;
TT getT()
{
return value;
@@ -51,4 +51,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
Empty<EmptyS> obj;
test(obj);
outputBuffer[dispatchThreadID.x] = dispatchThreadID.x;
-} \ No newline at end of file
+}
diff --git a/tests/compute/generic-closer.slang b/tests/compute/generic-closer.slang
index 377b42dab..c497b14d0 100644
--- a/tests/compute/generic-closer.slang
+++ b/tests/compute/generic-closer.slang
@@ -13,7 +13,7 @@ struct Gen0 : IGetter
};
struct Gen1<TGetter : IGetter> : IGetter
{
- TGetter g;
+ TGetter g = TGetter();
int get() { return g.get(); }
};
@@ -39,4 +39,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
int b = 5;
if (a< b && b > a)
outputBuffer[dispatchThreadID.x] = (g.get() >> 1) + g2.get() + g3.get();
-} \ No newline at end of file
+}
diff --git a/tests/compute/generic-default-arg.slang b/tests/compute/generic-default-arg.slang
index 8762f5e8a..3a3a4a5b8 100644
--- a/tests/compute/generic-default-arg.slang
+++ b/tests/compute/generic-default-arg.slang
@@ -31,7 +31,7 @@ struct Impl2 : ITest
__generic<T : ITest = Impl1>
struct GenStruct
{
- T obj;
+ T obj = T();
};
int test(GenStruct gs, int val)
@@ -50,4 +50,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
outVal += test(gs, tid);
outputBuffer[tid] = outVal;
-} \ No newline at end of file
+}
diff --git a/tests/compute/transitive-interface.slang b/tests/compute/transitive-interface.slang
index e4d7db91d..d8b167bd7 100644
--- a/tests/compute/transitive-interface.slang
+++ b/tests/compute/transitive-interface.slang
@@ -47,7 +47,7 @@ struct AssocImpl : IAssoc
int testAdd2<T:IAssoc>(T assoc)
{
- T.AT obj;
+ T.AT obj = T.AT();
return obj.addf(1, 1);
}
@@ -69,4 +69,4 @@ void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
outVal += testSub(s1, outVal);
outputBuffer[dispatchThreadID.x] = outVal;
-} \ No newline at end of file
+}
diff --git a/tests/cuda/cuda-array-layout.slang b/tests/cuda/cuda-array-layout.slang
index 60f318962..b99ab84c8 100644
--- a/tests/cuda/cuda-array-layout.slang
+++ b/tests/cuda/cuda-array-layout.slang
@@ -10,7 +10,7 @@ struct PadLadenStruct
};
// This is to check if the last half can be inserted 'inside' the spare padding of a. It should not be
-struct StructWithArray
+struct StructWithArray : IDefaultInitializable
{
PadLadenStruct a[1];
uint8_t b;
diff --git a/tests/diagnostics/interfaces/anyvalue-size-validation.slang b/tests/diagnostics/interfaces/anyvalue-size-validation.slang
index 1ebf7f4c3..f2ed52d0c 100644
--- a/tests/diagnostics/interfaces/anyvalue-size-validation.slang
+++ b/tests/diagnostics/interfaces/anyvalue-size-validation.slang
@@ -26,6 +26,6 @@ RWStructuredBuffer<uint> output;
[numthreads(4, 1, 1)]
void main()
{
- S s;
+ S s = S();
output[0] = test(s).a;
-} \ No newline at end of file
+}
diff --git a/tests/diagnostics/uninitialized-globals.slang b/tests/diagnostics/uninitialized-globals.slang
new file mode 100644
index 000000000..314881a80
--- /dev/null
+++ b/tests/diagnostics/uninitialized-globals.slang
@@ -0,0 +1,48 @@
+//TEST:SIMPLE(filecheck=CHK): -target spirv
+
+// 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;
+}
+
+//CHK-NOT: warning 41017
diff --git a/tests/diagnostics/uninitialized.slang b/tests/diagnostics/uninitialized-local-variables.slang
index 4779f45c9..5a2119f53 100644
--- a/tests/diagnostics/uninitialized.slang
+++ b/tests/diagnostics/uninitialized-local-variables.slang
@@ -1,14 +1,5 @@
//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)
{
@@ -157,107 +148,4 @@ float structs()
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
diff --git a/tests/diagnostics/uninitialized-out-parameters.slang b/tests/diagnostics/uninitialized-out-parameters.slang
new file mode 100644
index 000000000..9714a4b76
--- /dev/null
+++ b/tests/diagnostics/uninitialized-out-parameters.slang
@@ -0,0 +1,51 @@
+//TEST:SIMPLE(filecheck=CHK): -target spirv
+
+// 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 41018
diff --git a/tests/diagnostics/uninitialized-out.slang.expected b/tests/diagnostics/uninitialized-out.slang.expected
deleted file mode 100644
index 5846c12df..000000000
--- a/tests/diagnostics/uninitialized-out.slang.expected
+++ /dev/null
@@ -1,20 +0,0 @@
-result code = -1
-standard error = {
-tests/diagnostics/uninitialized-out.slang(6): error 41015: use of uninitialized value 'v'
- float r = v.x + 1.0;
- ^
-tests/diagnostics/uninitialized-out.slang(8): warning 41016: returning without initializing out parameter 'v'
- return r;
- ^~~~~~
-tests/diagnostics/uninitialized-out.slang(14): warning 41016: returning without initializing out parameter 'x'
- return 0;
- ^~~~~~
-tests/diagnostics/uninitialized-out.slang(18): warning 41016: returning without initializing out parameter 'x'
-void baz(out float x) {}
- ^
-tests/diagnostics/uninitialized-out.slang(25): warning 41016: returning without initializing out parameter 'y'
- return;
- ^~~~~~
-}
-standard output = {
-}
diff --git a/tests/diagnostics/uninitialized-use-functions.slang b/tests/diagnostics/uninitialized-use-functions.slang
new file mode 100644
index 000000000..82e7a04a8
--- /dev/null
+++ b/tests/diagnostics/uninitialized-use-functions.slang
@@ -0,0 +1,113 @@
+//TEST:SIMPLE(filecheck=CHK): -target spirv
+
+// Both out and inout parameters
+// should have this treated as writes
+void out_test(int tmp, out int x)
+{
+ x = tmp;
+}
+
+// Permuting arguments to ensure that
+// the correct argument is checked
+void inout_test(inout int x, int tmp)
+{
+ x = tmp;
+}
+
+// __ref parameters should also be fine
+void ref_test(__ref int x, int tmp)
+{
+ x = tmp;
+}
+
+void undefined_function_use()
+{
+ int x;
+ int tmp = 1;
+
+ //CHK-DAG: warning 41016: use of uninitialized variable 'x'
+ out_test(x, tmp);
+
+ //CHK-DAG: warning 41016: use of uninitialized variable 'x'
+ inout_test(tmp, x);
+}
+
+void out_function_use()
+{
+ int x;
+ int tmp = 1;
+
+ // Acts as a write
+ out_test(tmp, x);
+
+ // Rest are fine now
+ out_test(x, tmp);
+ inout_test(tmp, x);
+}
+
+void inout_function_use()
+{
+ int x;
+ int tmp = 1;
+
+ // Acts as a write
+ inout_test(x, tmp);
+
+ // Rest are fine now
+ out_test(x, tmp);
+ inout_test(tmp, x);
+}
+
+void ref_function_use()
+{
+ int x;
+ int tmp = 1;
+
+ // Acts as a write
+ ref_test(x, tmp);
+
+ // Rest are fine now
+ out_test(x, tmp);
+ inout_test(tmp, x);
+}
+
+// Likewise for generic functions
+static int ord_gen_result;
+
+__generic<T>
+void ordinary_generic(T x)
+{
+ ord_gen_result = __slang_noop_cast<int, T>(x);
+}
+
+__generic<T>
+void unordinary_generic(out T x)
+{
+ x = __slang_noop_cast<T, int>(1);
+}
+
+void undefined_generic_use()
+{
+ int x;
+
+ //CHK-DAG: warning 41016: use of uninitialized variable 'x'
+ ordinary_generic(x);
+}
+
+void ok_generic_use()
+{
+ int x;
+ unordinary_generic(x);
+ ordinary_generic(x);
+}
+
+// Check proper handling of aliases passed
+void f()
+{
+ int3 dim;
+
+ // Should have no warnings
+ out_test(1, dim.x);
+}
+
+//CHK-NOT: warning 41016
diff --git a/tests/language-feature/types/is-on-type.slang b/tests/language-feature/types/is-on-type.slang
index e9dd48fcf..728f759ad 100644
--- a/tests/language-feature/types/is-on-type.slang
+++ b/tests/language-feature/types/is-on-type.slang
@@ -18,7 +18,7 @@ struct A : I
struct B : I
{
- float2 f2;
+ float2 f2 = float2(0.0f);
};
func test<T : I>(T t) -> int
@@ -42,4 +42,4 @@ void computeMain()
B b;
// CHECK: 2
outputBuffer[0] = test(b);
-} \ No newline at end of file
+}