diff options
| author | Ellie Hermaszewska <ellieh@nvidia.com> | 2025-07-29 20:38:08 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-29 12:38:08 +0000 |
| commit | ea6f8551ad38f2bcc32b542fd52ce17f3829cbeb (patch) | |
| tree | 42a01a628d7d0c42255239c8e8eae8dbf681a616 /source/slang/slang-ir-detect-uninitialized-resources.cpp | |
| parent | 1da9019e9d3150502264365668156edf64ddfab1 (diff) | |
Detect uses of uninitialized resource fields (#7962)
Closes https://github.com/shader-slang/slang/issues/3386
Diffstat (limited to 'source/slang/slang-ir-detect-uninitialized-resources.cpp')
| -rw-r--r-- | source/slang/slang-ir-detect-uninitialized-resources.cpp | 237 |
1 files changed, 237 insertions, 0 deletions
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 |
