summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/slang/slang-diagnostic-defs.h19
-rw-r--r--source/slang/slang-emit.cpp3
-rw-r--r--source/slang/slang-ir-detect-uninitialized-resources.cpp237
-rw-r--r--source/slang/slang-ir-detect-uninitialized-resources.h9
-rw-r--r--tests/diagnostics/uninitialized-resource-field.slang26
5 files changed, 294 insertions, 0 deletions
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 07433df5c..5544329a5 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -43,6 +43,7 @@ DIAGNOSTIC(
"parameter type '$0'.")
DIAGNOSTIC(-1, Note, noteShaderIsTargetingPipeine, "shader '$0' is targeting pipeline '$1'")
DIAGNOSTIC(-1, Note, seeDefinitionOf, "see definition of '$0'")
+DIAGNOSTIC(-1, Note, seeDefinitionOfStruct, "see definition of struct '$0'")
DIAGNOSTIC(-1, Note, seeConstantBufferDefinition, "see constant buffer definition.")
DIAGNOSTIC(-1, Note, seeInterfaceDefinitionOf, "see interface definition of '$0'")
DIAGNOSTIC(-1, Note, seeUsingOf, "see using of '$0'")
@@ -2507,6 +2508,24 @@ DIAGNOSTIC(
"method marked `[mutable]` but never modifies `this`")
DIAGNOSTIC(
+ 41024,
+ Error,
+ cannotDefaultInitializeResource,
+ "cannot default-initialize $0 with '{}'. Resource types must be explicitly initialized")
+
+DIAGNOSTIC(
+ 41024,
+ Error,
+ cannotDefaultInitializeStructWithUninitializedResource,
+ "cannot default-initialize struct '$0' with '{}' because it contains an uninitialized $1 field")
+
+DIAGNOSTIC(
+ 41024,
+ Error,
+ cannotDefaultInitializeStructContainingResources,
+ "cannot default-initialize struct '$0' with '{}' because it contains resource fields")
+
+DIAGNOSTIC(
41011,
Error,
typeDoesNotFitAnyValueSize,
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index 067b5a551..b548ef632 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -35,6 +35,7 @@
#include "slang-ir-dce.h"
#include "slang-ir-defer-buffer-load.h"
#include "slang-ir-defunctionalization.h"
+#include "slang-ir-detect-uninitialized-resources.h"
#include "slang-ir-diff-call.h"
#include "slang-ir-dll-export.h"
#include "slang-ir-dll-import.h"
@@ -1102,6 +1103,8 @@ Result linkAndOptimizeIR(
break;
}
+ detectUninitializedResources(irModule, sink);
+
if (codeGenContext->removeAvailableInDownstreamIR)
{
removeAvailableInDownstreamModuleDecorations(target, irModule);
diff --git a/source/slang/slang-ir-detect-uninitialized-resources.cpp b/source/slang/slang-ir-detect-uninitialized-resources.cpp
new file mode 100644
index 000000000..dfadbe300
--- /dev/null
+++ b/source/slang/slang-ir-detect-uninitialized-resources.cpp
@@ -0,0 +1,237 @@
+// slang-ir-detect-uninitialized-resources.cpp
+//
+// This pass detects attempts to default-initialize resource types or structs containing
+// resource types using empty initializers ({}) or undefined values. This pattern is not
+// supported by downstream compilers like DXC.
+//
+// The pass detects empty makeStruct or undefined values being passed to constructors
+// where the parameter type contains resources.
+
+#include "slang-ir-detect-uninitialized-resources.h"
+
+#include "slang-compiler.h"
+#include "slang-ir-insts.h"
+#include "slang-ir-util.h"
+#include "slang-ir.h"
+
+namespace Slang
+{
+
+struct UninitializedResourceDetectionContext
+{
+ IRModule* module;
+ DiagnosticSink* sink;
+
+ // Cache of types we've already checked
+ InstHashSet structsWithResources;
+ Dictionary<IRType*, bool> typeContainsResourceCache;
+
+ UninitializedResourceDetectionContext(IRModule* module, DiagnosticSink* sink)
+ : module(module), sink(sink), structsWithResources(module)
+ {
+ }
+
+ // Check if a type is or contains a resource type
+ bool containsResourceType(IRType* type)
+ {
+ // Check cache first
+ if (auto cached = typeContainsResourceCache.tryGetValue(type))
+ return *cached;
+
+ bool result = false;
+
+ // Direct resource type
+ if (as<IRResourceType>(type))
+ {
+ result = true;
+ }
+ // Check struct types for resource fields
+ else if (auto structType = as<IRStructType>(type))
+ {
+ for (auto field : structType->getFields())
+ {
+ if (containsResourceType(field->getFieldType()))
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+ // Check array types
+ else if (auto arrayType = as<IRArrayType>(type))
+ {
+ result = containsResourceType(arrayType->getElementType());
+ }
+ // Check vector types
+ else if (auto vectorType = as<IRVectorType>(type))
+ {
+ result = containsResourceType(vectorType->getElementType());
+ }
+ // Check matrix types
+ else if (auto matrixType = as<IRMatrixType>(type))
+ {
+ result = containsResourceType(matrixType->getElementType());
+ }
+
+ // Cache the result
+ typeContainsResourceCache[type] = result;
+ return result;
+ }
+
+ // Get a human-readable name for a resource type
+ String getResourceTypeName(IRType* type)
+ {
+ if (as<IRTextureType>(type))
+ return "texture";
+ if (as<IRSamplerStateType>(type))
+ return "sampler";
+ if (as<IRHLSLStructuredBufferTypeBase>(type))
+ return "buffer";
+
+ return "resource";
+ }
+
+ // Get struct name if available
+ String getStructName(IRStructType* structType)
+ {
+ if (auto name = structType->findDecoration<IRNameHintDecoration>())
+ return String(name->getName());
+ return "<unnamed>";
+ }
+
+ // Check if an instruction is a problematic uninitialized value
+ bool isUninitializedResourceValue(IRInst* inst)
+ {
+ // Check for empty makeStruct
+ if (auto makeStruct = as<IRMakeStruct>(inst))
+ {
+ if (makeStruct->getOperandCount() == 0 &&
+ containsResourceType(makeStruct->getDataType()))
+ {
+ return true;
+ }
+ }
+
+ // Check for undefined values
+ if (as<IRUndefined>(inst))
+ {
+ if (containsResourceType(inst->getDataType()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Check if an instruction is a constructor call
+ bool isConstructorCall(IRCall* call)
+ {
+ if (auto callee = call->getCallee())
+ {
+ return callee->findDecoration<IRConstructorDecoration>() != nullptr;
+ }
+ return false;
+ }
+
+ void processInst(IRInst* inst)
+ {
+ // We're only interested in constructor calls
+ if (auto call = as<IRCall>(inst))
+ {
+ if (!isConstructorCall(call))
+ return;
+
+ // Check each argument
+ UInt argCount = call->getArgCount();
+ for (UInt i = 0; i < argCount; i++)
+ {
+ auto arg = call->getArg(i);
+ if (isUninitializedResourceValue(arg))
+ {
+ auto sourceLoc = call->sourceLoc;
+ auto uninitializedType = arg->getDataType();
+
+ // Get the type being constructed
+ IRType* constructedType = nullptr;
+ if (auto callee = call->getCallee())
+ {
+ if (auto funcType = as<IRFuncType>(callee->getDataType()))
+ {
+ constructedType = funcType->getResultType();
+ }
+ }
+
+ // If we're constructing a struct and passing an uninitialized resource
+ if (constructedType && as<IRStructType>(constructedType))
+ {
+ auto structType = as<IRStructType>(constructedType);
+ String structName = getStructName(structType);
+
+ if (as<IRResourceType>(uninitializedType))
+ {
+ String resourceName = getResourceTypeName(uninitializedType);
+
+ // Main error
+ sink->diagnose(
+ sourceLoc,
+ Diagnostics::cannotDefaultInitializeStructWithUninitializedResource,
+ structName,
+ resourceName);
+
+ // Note pointing to struct definition
+ if (structType->sourceLoc.isValid())
+ {
+ sink->diagnose(
+ structType->sourceLoc,
+ Diagnostics::seeDefinitionOfStruct,
+ structName);
+ }
+ }
+ else
+ {
+ // Struct contains nested resources
+ sink->diagnose(
+ sourceLoc,
+ Diagnostics::cannotDefaultInitializeStructContainingResources,
+ structName);
+ }
+ }
+ else
+ {
+ // Direct resource initialization
+ String resourceName = getResourceTypeName(uninitializedType);
+ sink->diagnose(
+ sourceLoc,
+ Diagnostics::cannotDefaultInitializeResource,
+ resourceName);
+ }
+ }
+ }
+ }
+ }
+
+ void processModule()
+ {
+ // Simple recursive traversal of the entire module
+ processInstRec(module->getModuleInst());
+ }
+
+ void processInstRec(IRInst* inst)
+ {
+ processInst(inst);
+
+ for (auto child : inst->getChildren())
+ {
+ processInstRec(child);
+ }
+ }
+};
+
+void detectUninitializedResources(IRModule* module, DiagnosticSink* sink)
+{
+ UninitializedResourceDetectionContext context(module, sink);
+ context.processModule();
+}
+
+} // namespace Slang
diff --git a/source/slang/slang-ir-detect-uninitialized-resources.h b/source/slang/slang-ir-detect-uninitialized-resources.h
new file mode 100644
index 000000000..8e4db0b61
--- /dev/null
+++ b/source/slang/slang-ir-detect-uninitialized-resources.h
@@ -0,0 +1,9 @@
+#pragma once
+
+namespace Slang
+{
+struct IRModule;
+class DiagnosticSink;
+
+void detectUninitializedResources(IRModule* module, DiagnosticSink* sink);
+} // namespace Slang
diff --git a/tests/diagnostics/uninitialized-resource-field.slang b/tests/diagnostics/uninitialized-resource-field.slang
new file mode 100644
index 000000000..b2e87effb
--- /dev/null
+++ b/tests/diagnostics/uninitialized-resource-field.slang
@@ -0,0 +1,26 @@
+//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK): -stage compute -entry computeMain -target hlsl
+
+RWStructuredBuffer<int> outputBuffer;
+
+void useStruct(SomeStruct s)
+{
+ outputBuffer[0] = s.value;
+}
+
+[shader("compute")]
+[numthreads(1,1,1)]
+void computeMain(uint3 threadId : SV_DispatchThreadID)
+{
+ // CHECK: ([[# @LINE+1]]): error {{.*}} cannot default-initialize struct 'SomeStruct' with '{}' because it contains an uninitialized texture field
+ SomeStruct s = {};
+
+ s.value = 10 + threadId.x;
+ useStruct(s);
+}
+
+// CHECK: ([[# @LINE+1]]): note: see definition of struct 'SomeStruct'
+struct SomeStruct
+{
+ int value;
+ Texture1D<float> tex;
+}