yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

CopilotFix findFunctionByNameInType to preserve functions with different signatures (#7827)0d26dbaad

master
13.3 KiB328 linesraw
1// unit-test-translation-unit-import.cpp
2
3#include "../../source/core/slang-io.h"
4#include "../../source/core/slang-process.h"
5#include "slang-com-ptr.h"
6#include "slang.h"
7#include "unit-test/slang-unit-test.h"
8
9#include <stdio.h>
10#include <stdlib.h>
11
12using namespace Slang;
13
14static String getTypeFullName(slang::TypeReflection* type)
15{
16    ComPtr<ISlangBlob> blob;
17    type->getFullName(blob.writeRef());
18    return String((const char*)blob->getBufferPointer());
19}
20
21// Test that the reflection API provides correct info about entry point and ordinary functions.
22
23SLANG_UNIT_TEST(functionReflection)
24{
25    // Source for a module that contains an undecorated entrypoint.
26    const char* userSourceBody = R"(
27        [__AttributeUsage(_AttributeTargets.Function)]
28        struct MyFuncPropertyAttribute {int v;}
29
30        [MyFuncProperty(1024)]
31        [Differentiable]
32        float ordinaryFunc(no_diff float x, int y) { return x + y; }
33
34        float4 fragMain(float4 pos:SV_Position) : SV_Position
35        {
36            return pos;
37        }
38
39        float foo(float x) { return x; }
40        float foo(float x, uint i) { return x + i; }
41
42        int bar1(IFloat a, IFloat b) { return 0; }
43        int bar2<T>(T a, float3 b) { return 0; }
44        int bar3(float3 b) { return 0; }
45        int bar4<T:IFloat>(T a){return 0;}
46
47        struct Foo { __init() {} }
48        )";
49
50    auto moduleName = "moduleG" + String(Process::getId());
51    String userSource = "import " + moduleName + ";\n" + userSourceBody;
52    ComPtr<slang::IGlobalSession> globalSession;
53    SLANG_CHECK(slang_createGlobalSession(SLANG_API_VERSION, globalSession.writeRef()) == SLANG_OK);
54    slang::TargetDesc targetDesc = {};
55    targetDesc.format = SLANG_HLSL;
56    targetDesc.profile = globalSession->findProfile("sm_5_0");
57    slang::SessionDesc sessionDesc = {};
58    sessionDesc.targetCount = 1;
59    sessionDesc.targets = &targetDesc;
60    ComPtr<slang::ISession> session;
61    SLANG_CHECK(globalSession->createSession(sessionDesc, session.writeRef()) == SLANG_OK);
62
63    ComPtr<slang::IBlob> diagnosticBlob;
64    auto module = session->loadModuleFromSourceString(
65        "m",
66        "m.slang",
67        userSourceBody,
68        diagnosticBlob.writeRef());
69    SLANG_CHECK(module != nullptr);
70
71    ComPtr<slang::IEntryPoint> entryPoint;
72    module->findAndCheckEntryPoint(
73        "fragMain",
74        SLANG_STAGE_FRAGMENT,
75        entryPoint.writeRef(),
76        diagnosticBlob.writeRef());
77    SLANG_CHECK(entryPoint != nullptr);
78
79    auto entryPointFuncReflection = entryPoint->getFunctionReflection();
80    SLANG_CHECK(entryPointFuncReflection != nullptr);
81    SLANG_CHECK(UnownedStringSlice(entryPointFuncReflection->getName()) == "fragMain");
82    SLANG_CHECK(entryPointFuncReflection->getParameterCount() == 1);
83    SLANG_CHECK(
84        UnownedStringSlice(entryPointFuncReflection->getParameterByIndex(0)->getName()) == "pos");
85    SLANG_CHECK(
86        getTypeFullName(entryPointFuncReflection->getParameterByIndex(0)->getType()) ==
87        "vector<float,4>");
88
89    auto funcReflection = module->getLayout()->findFunctionByName("ordinaryFunc");
90    SLANG_CHECK(funcReflection != nullptr);
91
92    SLANG_CHECK(funcReflection->findModifier(slang::Modifier::Differentiable) != nullptr);
93    SLANG_CHECK(getTypeFullName(funcReflection->getReturnType()) == "float");
94    SLANG_CHECK(UnownedStringSlice(funcReflection->getName()) == "ordinaryFunc");
95    SLANG_CHECK(funcReflection->getParameterCount() == 2);
96    SLANG_CHECK(UnownedStringSlice(funcReflection->getParameterByIndex(0)->getName()) == "x");
97    SLANG_CHECK(getTypeFullName(funcReflection->getParameterByIndex(0)->getType()) == "float");
98    SLANG_CHECK(
99        funcReflection->getParameterByIndex(0)->findModifier(slang::Modifier::NoDiff) != nullptr);
100
101    SLANG_CHECK(UnownedStringSlice(funcReflection->getParameterByIndex(1)->getName()) == "y");
102    SLANG_CHECK(getTypeFullName(funcReflection->getParameterByIndex(1)->getType()) == "int");
103
104    SLANG_CHECK(funcReflection->getUserAttributeCount() == 1);
105    auto userAttribute = funcReflection->getUserAttributeByIndex(0);
106    SLANG_CHECK(UnownedStringSlice(userAttribute->getName()) == "MyFuncProperty");
107    SLANG_CHECK(userAttribute->getArgumentCount() == 1);
108    SLANG_CHECK(getTypeFullName(userAttribute->getArgumentType(0)) == "int");
109    int val = 0;
110    auto result = userAttribute->getArgumentValueInt(0, &val);
111    SLANG_CHECK(result == SLANG_OK);
112    SLANG_CHECK(val == 1024);
113    SLANG_CHECK(
114        funcReflection->findAttributeByName(globalSession.get(), "MyFuncProperty") ==
115        userAttribute);
116
117    // Check overloaded method resolution
118    auto overloadReflection = module->getLayout()->findFunctionByName("foo");
119    SLANG_CHECK(overloadReflection != nullptr);
120    SLANG_CHECK(overloadReflection->isOverloaded() == true);
121    SLANG_CHECK(overloadReflection->getOverloadCount() == 2);
122
123    auto firstOverload = overloadReflection->getOverload(0);
124    SLANG_CHECK(firstOverload != nullptr);
125    SLANG_CHECK(UnownedStringSlice(firstOverload->getName()) == "foo");
126    SLANG_CHECK(firstOverload->getParameterCount() == 2);
127    SLANG_CHECK(UnownedStringSlice(firstOverload->getParameterByIndex(0)->getName()) == "x");
128    SLANG_CHECK(getTypeFullName(firstOverload->getParameterByIndex(0)->getType()) == "float");
129    SLANG_CHECK(UnownedStringSlice(firstOverload->getParameterByIndex(1)->getName()) == "i");
130    SLANG_CHECK(getTypeFullName(firstOverload->getParameterByIndex(1)->getType()) == "uint");
131
132    auto secondOverload = overloadReflection->getOverload(1);
133    SLANG_CHECK(secondOverload != nullptr);
134    SLANG_CHECK(UnownedStringSlice(secondOverload->getName()) == "foo");
135    SLANG_CHECK(secondOverload->getParameterCount() == 1);
136    SLANG_CHECK(UnownedStringSlice(secondOverload->getParameterByIndex(0)->getName()) == "x");
137
138    // Check overload resolution via argument types.
139    slang::TypeReflection* argTypes[] = {
140        module->getLayout()->findTypeByName("float"),
141        module->getLayout()->findTypeByName("uint"),
142    };
143    auto resolvedFunctionReflection = overloadReflection->specializeWithArgTypes(2, argTypes);
144    SLANG_CHECK(resolvedFunctionReflection == firstOverload);
145
146    //
147    // More testing for specializeWithArgTypes
148    //
149
150    // bar1 (IFloat, IFloat) -> int
151    //
152    auto bar1Reflection = module->getLayout()->findFunctionByName("bar1");
153    SLANG_CHECK(bar1Reflection != nullptr);
154    SLANG_CHECK(bar1Reflection->isOverloaded() == false);
155    SLANG_CHECK(bar1Reflection->getParameterCount() == 2);
156
157    auto float3Type = module->getLayout()->findTypeByName("float3");
158    SLANG_CHECK(float3Type != nullptr);
159    argTypes[0] = float3Type;
160    argTypes[1] = float3Type;
161
162    resolvedFunctionReflection = bar1Reflection->specializeWithArgTypes(2, argTypes);
163
164    SLANG_CHECK(resolvedFunctionReflection != nullptr);
165    SLANG_CHECK(resolvedFunctionReflection->getParameterCount() == 2);
166    SLANG_CHECK(
167        getTypeFullName(resolvedFunctionReflection->getParameterByIndex(0)->getType()) == "IFloat");
168    SLANG_CHECK(
169        getTypeFullName(resolvedFunctionReflection->getParameterByIndex(1)->getType()) == "IFloat");
170
171    // bar2 (T : IFloat, float3) -> int
172    //
173    auto bar2Reflection = module->getLayout()->findFunctionByName("bar2");
174    SLANG_CHECK(bar2Reflection != nullptr);
175    SLANG_CHECK(bar2Reflection->isOverloaded() == false);
176    SLANG_CHECK(bar2Reflection->getParameterCount() == 2);
177
178    auto floatType = module->getLayout()->findTypeByName("float");
179    SLANG_CHECK(floatType != nullptr);
180    argTypes[0] = floatType;
181    argTypes[1] = float3Type;
182
183    resolvedFunctionReflection = bar2Reflection->specializeWithArgTypes(2, argTypes);
184
185    SLANG_CHECK(resolvedFunctionReflection != nullptr);
186    SLANG_CHECK(resolvedFunctionReflection->getParameterCount() == 2);
187    SLANG_CHECK(
188        getTypeFullName(resolvedFunctionReflection->getParameterByIndex(0)->getType()) == "float");
189    SLANG_CHECK(
190        getTypeFullName(resolvedFunctionReflection->getParameterByIndex(1)->getType()) ==
191        "vector<float,3>");
192
193
194    // failure case
195    argTypes[0] = floatType;
196    argTypes[1] = module->getLayout()->findTypeByName("float2");
197    resolvedFunctionReflection = bar2Reflection->specializeWithArgTypes(2, argTypes);
198    SLANG_CHECK(resolvedFunctionReflection == nullptr); // any errors should result in a nullptr.
199
200    // bar3 (float3) -> int
201    // (trivial case)
202    auto bar3Reflection = module->getLayout()->findFunctionByName("bar3");
203    SLANG_CHECK(bar3Reflection != nullptr);
204    SLANG_CHECK(bar3Reflection->isOverloaded() == false);
205    SLANG_CHECK(bar3Reflection->getParameterCount() == 1);
206
207    argTypes[0] = float3Type;
208    resolvedFunctionReflection = bar3Reflection->specializeWithArgTypes(1, argTypes);
209    SLANG_CHECK(resolvedFunctionReflection != nullptr);
210    SLANG_CHECK(resolvedFunctionReflection == bar3Reflection);
211
212    // GitHub issue #6317: bar2 is a function, not a type, so it should not be found.
213    SLANG_CHECK(module->getLayout()->findTypeByName("bar4") == nullptr);
214
215    auto fooType = module->getLayout()->findTypeByName("Foo");
216    SLANG_CHECK_ABORT(fooType != nullptr);
217    auto ctor = module->getLayout()->findFunctionByNameInType(fooType, "$init");
218    SLANG_CHECK(ctor != nullptr);
219}
220
221// Test that findFunctionByNameInType finds all functions with the same name but different
222// signatures
223SLANG_UNIT_TEST(findFunctionByNameInType)
224{
225    // Test shader with extensions that have functions with same name but different signatures
226    const char* userSourceBody = R"(
227        public interface IModel<float:IDifferentiable>
228        {
229            public float forward(float x);
230        }
231
232        interface IScalarActivation<float:IDifferentiable> {}
233
234        public extension<float:IDifferentiable, Act:IScalarActivation<float>> Act: IModel<float>
235        {
236            public float forward(float x) { return x;}
237        }
238
239        public struct MyStruct<float:IDifferentiable>: IScalarActivation<float> {}
240
241        public extension<float:IDifferentiable> MyStruct<float>: IModel<float[2]>
242        {
243            public float[2] forward(float[2] x) { return x;}
244        }
245
246        [shader("compute")]
247        void computeMain(uint3 tid: SV_DispatchThreadID)
248        {
249        }
250        )";
251
252    auto moduleName = "moduleH" + String(Process::getId());
253    String userSource = "import " + moduleName + ";\n" + userSourceBody;
254    ComPtr<slang::IGlobalSession> globalSession;
255    SLANG_CHECK(slang_createGlobalSession(SLANG_API_VERSION, globalSession.writeRef()) == SLANG_OK);
256    slang::TargetDesc targetDesc = {};
257    targetDesc.format = SLANG_HLSL;
258    targetDesc.profile = globalSession->findProfile("sm_5_0");
259    slang::SessionDesc sessionDesc = {};
260    sessionDesc.targetCount = 1;
261    sessionDesc.targets = &targetDesc;
262    ComPtr<slang::ISession> session;
263    SLANG_CHECK(globalSession->createSession(sessionDesc, session.writeRef()) == SLANG_OK);
264
265    ComPtr<slang::IBlob> diagnosticBlob;
266    auto module = session->loadModuleFromSourceString(
267        "test_module",
268        "test_module.slang",
269        userSourceBody,
270        diagnosticBlob.writeRef());
271    SLANG_CHECK(module != nullptr);
272
273    auto myStructType = module->getLayout()->findTypeByName("MyStruct<float>");
274    SLANG_CHECK_ABORT(myStructType != nullptr);
275
276    // Try to find the "forward" function in MyStruct<float>
277    // This should find functions with different signatures from both extensions:
278    // 1. float forward(float x) from the generic extension Act: IModel<float>
279    // 2. float[2] forward(float[2] x) from the MyStruct-specific extension MyStruct<float>:
280    // IModel<float[2]>
281    auto forwardFunc = module->getLayout()->findFunctionByNameInType(myStructType, "forward");
282
283    // With the fix, this should find functions with different signatures
284    SLANG_CHECK(forwardFunc != nullptr);
285
286    // The function should be overloaded since there are multiple functions with different
287    // signatures
288    if (forwardFunc->isOverloaded())
289    {
290        // If it's overloaded, verify we can access both variants
291        SLANG_CHECK(forwardFunc->getOverloadCount() >= 2);
292
293        // We should be able to find both:
294        // - One with float parameter type (from generic extension)
295        // - One with float[2] parameter type (from MyStruct-specific extension)
296        bool foundFloatParam = false;
297        bool foundFloatArrayParam = false;
298
299        for (int i = 0; i < forwardFunc->getOverloadCount(); i++)
300        {
301            auto overload = forwardFunc->getOverload(i);
302            // Check that each overload has the correct name
303            SLANG_CHECK(UnownedStringSlice(overload->getName()) == "forward");
304            if (overload->getParameterCount() > 0)
305            {
306                auto paramTypeName = overload->getParameterByIndex(0)->getType()->getName();
307                if (strstr(paramTypeName, "Array"))
308                {
309                    foundFloatArrayParam = true;
310                }
311                else if (strstr(paramTypeName, "float"))
312                {
313                    foundFloatParam = true;
314                }
315            }
316        }
317
318        // Both variants should be found
319        SLANG_CHECK(foundFloatParam);
320        SLANG_CHECK(foundFloatArrayParam);
321    }
322    else
323    {
324        // The function should be overloaded since there are multiple functions with different
325        // signatures. If it's not overloaded, the fix didn't work properly.
326        SLANG_CHECK_ABORT(false && "Expected function to be overloaded with multiple signatures");
327    }
328}