summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-ir-detect-uninitialized-resources.cpp
blob: dfadbe3007ec9ef866b969ce88010e072b5543d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
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