From b78a8ba006fc9253cd1fd88fb7dd1eacfa749dfa Mon Sep 17 00:00:00 2001 From: "Harsh Aggarwal (NVIDIA)" Date: Thu, 24 Apr 2025 10:11:30 +0530 Subject: Fix #6544: Properly format nested type names in extensions (#6769) * Fix #6544: Properly format nested type names in extensions Modify DeclRefBase::toText to properly handle types defined in extensions by qualifying them with their parent type name. This ensures getFullName() returns the full name like 'FullPrecisionOptimizer.State' instead of just '.State'. Also handle other nested types in structs/classes similarly. * Update extension reflection handling - with generics args and namespaces - stopping namespace inclusion for extension members - Update to use getTargetType() to handle the generic arguments - update test cases * Simplify code to remove using parentDecl --- tests/diagnostics/extension-full-name.slang | 357 +++++++++++++++++++++ tests/diagnostics/extension-full-name.slang.actual | 32 ++ .../diagnostics/extension-full-name.slang.expected | 41 +++ 3 files changed, 430 insertions(+) create mode 100644 tests/diagnostics/extension-full-name.slang create mode 100644 tests/diagnostics/extension-full-name.slang.actual create mode 100644 tests/diagnostics/extension-full-name.slang.expected (limited to 'tests') diff --git a/tests/diagnostics/extension-full-name.slang b/tests/diagnostics/extension-full-name.slang new file mode 100644 index 000000000..292028011 --- /dev/null +++ b/tests/diagnostics/extension-full-name.slang @@ -0,0 +1,357 @@ +// extension-full-name.slang +//DIAGNOSTIC_TEST:SIMPLE:-target hlsl + +// Define a generic type with a nested struct +struct GenericType +{ + T value; + + struct InnerType + { + T innerValue; + } + + InnerType inner; +} + +// Define a non-generic type +struct NonGenericType +{ + float value; + + struct InnerType + { + float innerValue; + } + + InnerType inner; +} + +// Add an extension to the generic type +extension GenericType +{ + // Type in extension + struct State + { + half value; + half factor; + } + + // Function in extension + State + createState() + { + State state; + state.value = value; + state.factor = 1.0h; + return state; + } + + // Value/field in extension + static State defaultState; +} + +// Add an extension to a nested type inside a generic type +extension GenericType.InnerType +{ + struct Options + { + float min; + float max; + } + + Options + getOptions() + { + Options opts; + opts.min = 0; + opts.max = innerValue; + return opts; + } + + static Options defaultOptions; +} + +// Add an extension to the non-generic type +extension NonGenericType +{ + // Type in extension + struct Config + { + float threshold; + float scale; + } + + // Function in extension + Config + createConfig() + { + Config config; + config.threshold = 0.5; + config.scale = 2.0; + return config; + } + + // Value/field in extension + static Config defaultConfig; +} + +// Add nested types and extensions +struct Container +{ + struct Nested + { + float value; + + struct DeepNested + { + int count; + } + + DeepNested deep; + } + + Nested nested; +} + +// Extension for deeply nested type +extension Container.Nested.DeepNested +{ + struct Record + { + int id; + float value; + } + + Record + createRecord(int newId) + { + Record r; + r.id = newId; + r.value = 0; + return r; + } + + static Record defaultRecord; +} + +extension Container.Nested +{ + // Type in nested extension + struct Settings + { + float value; + float threshold; + } + + // Function in nested extension + Settings + createSettings() + { + Settings settings; + settings.value = value; + settings.threshold = 0.1; + return settings; + } + + // Value/field in nested extension + static Settings defaultSettings; +} + +[shader("compute")][numthreads(1, 1, 1)] void main(uint3 dispatchThreadID + : SV_DispatchThreadID) +{ + // Test array extensions with constraints + testArrayExtension(); + + // Test namespace extensions + testNamespaceExtensions(); + + // Type instantiation tests + GenericType.State state1; + GenericType.InnerType.Options options1; + NonGenericType.Config config1; + Container.Nested.Settings settings1; + Container.Nested.DeepNested.Record record1; + + // Initialize extension types with struct literals + state1 = {1.0h, 2.0h}; // Valid struct initialization + options1 = {0.0, 1.0}; // Valid struct initialization + config1 = {0.1, 0.5}; // Valid struct initialization + settings1 = {0.2, 0.3}; // Valid struct initialization + record1 = {1, 2.5}; // Valid struct initialization + + // Extension type mismatches - assign value type to extension type + state1 = 0; // Error: expected expr of type 'GenericType.State', got 'int' + options1 = 0; // Error: expected expr of type 'GenericType.InnerType.Options', got 'int' + config1 = 0; // Error: expected expr of type 'NonGenericType.Config', got 'int' + settings1 = 0; // Error: expected expr of type 'Container.Nested.Settings', got 'int' + record1 = 0; // Error: expected expr of type 'Container.Nested.DeepNested.Record', got 'int' + + // Extension type mismatches - assign wrong extension types + GenericType.InnerType intInner; + GenericType.InnerType floatInner; + + // This should fail due to different generic parameters + floatInner.Options floatOpts = + intInner.getOptions(); // This won't compile as intInner doesn't have getOptions + + // Extension member access - valid cases + state1.value = 1.0h; // Valid + state1.factor = 2.0h; // Valid + options1.min = 0.0; // Valid + options1.max = 1.0; // Valid + config1.threshold = 0.5; // Valid + config1.scale = 2.0; // Valid + settings1.value = 0.1; // Valid + settings1.threshold = 0.2; // Valid + record1.id = 100; // Valid + record1.value = 3.14; // Valid + + // Extension function calls + GenericType halfType; + NonGenericType nonGenType; + Container.Nested nested; + Container.Nested.DeepNested deepNested; + GenericType.InnerType floatInnerType; + + state1 = halfType.createState(); // Valid + config1 = nonGenType.createConfig(); // Valid + settings1 = nested.createSettings(); // Valid + record1 = deepNested.createRecord(42); // Valid + options1 = floatInnerType.getOptions(); // Valid + + // Type mismatches - function return values + options1 = + halfType + .createState(); // Error: expected expr of type 'GenericType.InnerType.Options', + // got 'GenericType.State' + state1 = nonGenType.createConfig(); // Error: expected expr of type 'GenericType.State', + // got 'NonGenericType.Config' + config1 = nested.createSettings(); // Error: expected expr of type 'NonGenericType.Config', got + // 'Container.Nested.Settings' +} + +// Define an interface for constraint +interface IFoo +{ + float getValue(); +} + +// Struct that implements IFoo +struct Bar : IFoo +{ + float value; + + float getValue() { return value; } +} + +// Struct that doesn't implement IFoo +struct Baz +{ + float value; +} + +// Extension with constrained generic type parameter +extension Array +{ + // Add a struct type inside the extension + struct DataStats + { + float average; + float maximum; + float minimum; + } + + // Add a function that uses the struct type + DataStats computeStats() + { + DataStats stats; + stats.average = 0; + stats.maximum = -1e38; + stats.minimum = 1e38; + + for(int i = 0; i < N; i++) + { + float val = this[i].getValue(); + stats.average += val; + stats.maximum = max(stats.maximum, val); + stats.minimum = min(stats.minimum, val); + } + + if(N > 0) + stats.average /= float(N); + + return stats; + } + + // Add a field + static DataStats defaultStats; +} + +void testArrayExtension() +{ + // Create an array of a type that implements IFoo + Bar barArray[3] = { {1.0}, {2.0}, {3.0} }; + + // Create an array of a type that doesn't implement IFoo + Baz bazArray[3] = { {1.0}, {2.0}, {3.0} }; + + // This should work - using the extension on a valid type + Array.DataStats barStats; + barStats = barArray.computeStats(); // Valid + + // Type mismatch errors + barStats = 0; // Error: expected expr of type 'Bar[3].DataStats', got 'int' + + // This won't compile - Baz doesn't implement IFoo + //Array.DataStats bazStats; // This should fail because Baz doesn't implement IFoo + //bazStats = bazArray.computeStats(); // This also won't compile + + // Type mismatch between different extension instantiations + Array.DataStats bar2Stats; + bar2Stats = barArray.computeStats(); // Error: expected expr of type 'Bar[2].DataStats', got 'Bar[3].DataStats' +} + +// Test namespace extensions +// Define a simple base struct +struct SimpleBase +{ + float value; +} + +// Define a namespace with an extension +namespace TestNamespace +{ + extension SimpleBase + { + struct NamedConfig + { + float factor; + } + + NamedConfig createConfig() + { + NamedConfig config; + config.factor = value * 2.0; + return config; + } + } +} + +// Test function for namespace extensions +void testNamespaceExtensions() +{ + // Need to use the namespace to access the extension methods + using namespace TestNamespace; + + SimpleBase base; + base.value = 5.0; + + // The type is just "SimpleBase.NamedConfig", not "TestNamespace.SimpleBase.NamedConfig" + SimpleBase.NamedConfig config; + config.factor = 2.0; + + // Generate a type error to check the output format + config = 0; // Error: expected expr of type 'SimpleBase.NamedConfig', got 'int' +} diff --git a/tests/diagnostics/extension-full-name.slang.actual b/tests/diagnostics/extension-full-name.slang.actual new file mode 100644 index 000000000..0d53a90ce --- /dev/null +++ b/tests/diagnostics/extension-full-name.slang.actual @@ -0,0 +1,32 @@ +result code = -1 +standard error = { +tests/diagnostics/extension-full-name.slang(179): error 30019: expected an expression of type 'GenericType.State', got 'int' + state1 = 0; // Error: expected expr of type 'GenericType.State', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(180): error 30019: expected an expression of type 'GenericType.InnerType.Options', got 'int' + options1 = 0; // Error: expected expr of type 'GenericType.InnerType.Options', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(181): error 30019: expected an expression of type 'NonGenericType.Config', got 'int' + config1 = 0; // Error: expected expr of type 'NonGenericType.Config', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(182): error 30019: expected an expression of type 'Container.Nested.Settings', got 'int' + settings1 = 0; // Error: expected expr of type 'Container.Nested.Settings', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(183): error 30019: expected an expression of type 'Container.Nested.DeepNested.Record', got 'int' + record1 = 0; // Error: expected expr of type 'Container.Nested.DeepNested.Record', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(191): error 30027: 'getOptions' is not a member of 'GenericType.InnerType'. + intInner.getOptions(); // This won't compile as intInner doesn't have getOptions + ^~~~~~~~~~ +tests/diagnostics/extension-full-name.slang(221): error 30019: expected an expression of type 'GenericType.InnerType.Options', got 'GenericType.State' + .createState(); // Error: expected expr of type 'GenericType.InnerType.Options', + ^ +tests/diagnostics/extension-full-name.slang(223): error 30019: expected an expression of type 'GenericType.State', got 'NonGenericType.Config' + state1 = nonGenType.createConfig(); // Error: expected expr of type 'GenericType.State', + ^ +tests/diagnostics/extension-full-name.slang(225): error 30019: expected an expression of type 'NonGenericType.Config', got 'Container.Nested.Settings' + config1 = nested.createSettings(); // Error: expected expr of type 'NonGenericType.Config', got + ^ +} +standard output = { +} diff --git a/tests/diagnostics/extension-full-name.slang.expected b/tests/diagnostics/extension-full-name.slang.expected new file mode 100644 index 000000000..8001298c1 --- /dev/null +++ b/tests/diagnostics/extension-full-name.slang.expected @@ -0,0 +1,41 @@ +result code = -1 +standard error = { +tests/diagnostics/extension-full-name.slang(184): error 30019: expected an expression of type 'GenericType.State', got 'int' + state1 = 0; // Error: expected expr of type 'GenericType.State', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(185): error 30019: expected an expression of type 'GenericType.InnerType.Options', got 'int' + options1 = 0; // Error: expected expr of type 'GenericType.InnerType.Options', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(186): error 30019: expected an expression of type 'NonGenericType.Config', got 'int' + config1 = 0; // Error: expected expr of type 'NonGenericType.Config', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(187): error 30019: expected an expression of type 'Container.Nested.Settings', got 'int' + settings1 = 0; // Error: expected expr of type 'Container.Nested.Settings', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(188): error 30019: expected an expression of type 'Container.Nested.DeepNested.Record', got 'int' + record1 = 0; // Error: expected expr of type 'Container.Nested.DeepNested.Record', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(196): error 30027: 'getOptions' is not a member of 'GenericType.InnerType'. + intInner.getOptions(); // This won't compile as intInner doesn't have getOptions + ^~~~~~~~~~ +tests/diagnostics/extension-full-name.slang(226): error 30019: expected an expression of type 'GenericType.InnerType.Options', got 'GenericType.State' + .createState(); // Error: expected expr of type 'GenericType.InnerType.Options', + ^ +tests/diagnostics/extension-full-name.slang(228): error 30019: expected an expression of type 'GenericType.State', got 'NonGenericType.Config' + state1 = nonGenType.createConfig(); // Error: expected expr of type 'GenericType.State', + ^ +tests/diagnostics/extension-full-name.slang(230): error 30019: expected an expression of type 'NonGenericType.Config', got 'Container.Nested.Settings' + config1 = nested.createSettings(); // Error: expected expr of type 'NonGenericType.Config', got + ^ +tests/diagnostics/extension-full-name.slang(304): error 30019: expected an expression of type 'Bar[3].DataStats', got 'int' + barStats = 0; // Error: expected expr of type 'Bar[3].DataStats', got 'int' + ^ +tests/diagnostics/extension-full-name.slang(312): error 30019: expected an expression of type 'Bar[2].DataStats', got 'Bar[3].DataStats' + bar2Stats = barArray.computeStats(); // Error: expected expr of type 'Bar[2].DataStats', got 'Bar[3].DataStats' + ^ +tests/diagnostics/extension-full-name.slang(355): error 30019: expected an expression of type 'SimpleBase.NamedConfig', got 'int' + config = 0; // Error: expected expr of type 'SimpleBase.NamedConfig', got 'int' + ^ +} +standard output = { +} -- cgit v1.2.3