From 95767ea8ed565cba470f3de7cc8126db71dcb99d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 07:29:50 +0000 Subject: [PATCH 1/6] Initial plan From d69ebcd89e7e14b42e30177fc3b3de3378d2c734 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 07:54:46 +0000 Subject: [PATCH 2/6] Add Array.Initialize fix and test for NativeAOT - Modified UsageBasedMetadataManager.cs to add metadata dependency for array element type default constructors - Added TestArrayInitialize test to verify Array.Initialize works with value types having parameterless constructors Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../Compiler/UsageBasedMetadataManager.cs | 16 ++++++ .../SmokeTests/Reflection/Reflection.cs | 52 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 9979a0deb8cc43..5ab4737b5a5f10 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -311,6 +311,22 @@ protected override void GetMetadataDependenciesDueToReflectability(ref Dependenc } } + if (type.IsArray) + { + // Array.Initialize needs the default constructor of the element type to be reflectable + // for value types with a public parameterless constructor. + TypeDesc elementType = ((ArrayType)type).ParameterType; + if (elementType.IsValueType) + { + MethodDesc defaultConstructor = elementType.GetDefaultConstructor(); + if (defaultConstructor is not null && !IsReflectionBlocked(defaultConstructor)) + { + dependencies ??= new DependencyList(); + dependencies.Add(factory.ReflectedMethod(defaultConstructor.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Array.Initialize needs default constructor"); + } + } + } + MetadataType mdType = type as MetadataType; ModuleDesc module = mdType?.Module; if (module != null && !_rootEntireAssembliesExaminedModules.Contains(module)) diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index 10a21b4d59e236..a4e35748b2fdb4 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -99,6 +99,7 @@ private static int Main() TestGenericAttributesOnEnum.Run(); TestLdtokenWithSignaturesDifferingInModifiers.Run(); TestActivatingThingsInSignature.Run(); + TestArrayInitialize.Run(); TestDelegateInvokeFromEvent.Run(); return 100; @@ -3004,6 +3005,57 @@ public struct MyStruct; public struct MyArrayElementStruct; } + class TestArrayInitialize + { + static int s_constructorCallCount = 0; + + public struct ValueTypeWithConstructor + { + public int Value; + + public ValueTypeWithConstructor() + { + s_constructorCallCount++; + Value = 42; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void AllocateArray() + { + // Create an array to trigger the reflectability of the type + var array = new ValueTypeWithConstructor[5]; + } + + public static void Run() + { + Console.WriteLine(nameof(TestArrayInitialize)); + + // Ensure the type is in the static callgraph + if (string.Empty.Length > 0) + { + AllocateArray(); + } + + s_constructorCallCount = 0; + + // Create an array and call Initialize + var array = new ValueTypeWithConstructor[3]; + array.Initialize(); + + // Verify that the constructor was called for each element + if (s_constructorCallCount != 3) + throw new Exception($"Expected constructor to be called 3 times, but was called {s_constructorCallCount} times"); + + // Verify that each element has the expected value + for (int i = 0; i < array.Length; i++) + { + if (array[i].Value != 42) + throw new Exception($"Expected array[{i}].Value to be 42, but was {array[i].Value}"); + } + } + } + class TestDelegateInvokeFromEvent { class MyClass From fb0cfede7de7278b1aa51eb04c6b9b7886dc958c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 20 Nov 2025 01:40:22 -0800 Subject: [PATCH 3/6] Apply suggestions from code review --- src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index a4e35748b2fdb4..520892bd9e8460 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -3021,11 +3021,7 @@ public ValueTypeWithConstructor() } [MethodImpl(MethodImplOptions.NoInlining)] - static void AllocateArray() - { - // Create an array to trigger the reflectability of the type - var array = new ValueTypeWithConstructor[5]; - } + static Array AllocateArray() => new ValueTypeWithConstructor[5]; public static void Run() { @@ -3040,7 +3036,7 @@ public static void Run() s_constructorCallCount = 0; // Create an array and call Initialize - var array = new ValueTypeWithConstructor[3]; + var array = AllocateArray(); array.Initialize(); // Verify that the constructor was called for each element From fc051d3f764a94e5806b98db806ee92a4202052a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 20 Nov 2025 08:25:37 -0800 Subject: [PATCH 4/6] Refactor ValueTypeWithConstructor by removing Value field Removed unused Value field and related checks from ValueTypeWithConstructor. --- .../SmokeTests/Reflection/Reflection.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index 520892bd9e8460..da4a27eeae6f2a 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -3011,12 +3011,9 @@ class TestArrayInitialize public struct ValueTypeWithConstructor { - public int Value; - public ValueTypeWithConstructor() { s_constructorCallCount++; - Value = 42; } } @@ -3027,12 +3024,6 @@ public static void Run() { Console.WriteLine(nameof(TestArrayInitialize)); - // Ensure the type is in the static callgraph - if (string.Empty.Length > 0) - { - AllocateArray(); - } - s_constructorCallCount = 0; // Create an array and call Initialize @@ -3042,13 +3033,6 @@ public static void Run() // Verify that the constructor was called for each element if (s_constructorCallCount != 3) throw new Exception($"Expected constructor to be called 3 times, but was called {s_constructorCallCount} times"); - - // Verify that each element has the expected value - for (int i = 0; i < array.Length; i++) - { - if (array[i].Value != 42) - throw new Exception($"Expected array[{i}].Value to be 42, but was {array[i].Value}"); - } } } From fa13da7f8492956abd36af168b3fc2fac02d304a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 20 Nov 2025 18:27:09 +0100 Subject: [PATCH 5/6] Change array size in AllocateArray method --- src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index da4a27eeae6f2a..c361b46a785d3f 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -3018,7 +3018,7 @@ public ValueTypeWithConstructor() } [MethodImpl(MethodImplOptions.NoInlining)] - static Array AllocateArray() => new ValueTypeWithConstructor[5]; + static Array AllocateArray() => new ValueTypeWithConstructor[3]; public static void Run() { From 80b4cc8cebb63df836f22cff7df65eb1ee3dba9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 20 Nov 2025 13:15:19 -0800 Subject: [PATCH 6/6] Update src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 5ab4737b5a5f10..53e9b8727ded76 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -315,7 +315,7 @@ protected override void GetMetadataDependenciesDueToReflectability(ref Dependenc { // Array.Initialize needs the default constructor of the element type to be reflectable // for value types with a public parameterless constructor. - TypeDesc elementType = ((ArrayType)type).ParameterType; + TypeDesc elementType = ((ArrayType)type).ElementType; if (elementType.IsValueType) { MethodDesc defaultConstructor = elementType.GetDefaultConstructor();