From 9d7d7bc3a6ad06907ac894a1dc430bb188d5ef44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 19 Jun 2024 14:02:18 +0200 Subject: [PATCH] Always create constructed MethodTable for MdArray & co. Arrays that don't implement generic interface methods can be loaded at runtime without any sort of type loader template (their template is the same thing that we just whack into the right shape and there's no associated code because everything is from `System.Array`). But this means we should never create unconstructed MethodTable for these because we could end up in a situation where a single type has two `MethodTable`s. The regression test demonstrates how that can happen (the `is` check only forces unconstructed form of the `MethodTable`). Ran into this while working on a different PR, but I don't want to clutter that one with this. --- .../Compiler/DependencyAnalysis/EETypeNode.cs | 13 +++++++++++++ .../SmokeTests/Reflection/Reflection.cs | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index af1542186494d5..10bac5be472fe2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -624,6 +624,19 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact if (_type.IsInterface) dependencies.Add(factory.InterfaceUse(_type.GetTypeDefinition()), "Interface is used"); + // Array types that don't have generic interface methods can be created out of thin air + // at runtime by the type loader. We should never emit non-constructed forms of these MethodTables. + // There's similar logic for generic types, but that one is a conditional dependency conditioned + // on the presence of the type loader template for the canonical form of the type. + if (_type.IsArrayTypeWithoutGenericInterfaces()) + { + IEETypeNode maximallyConstructableType = factory.MaximallyConstructableType(_type); + if (maximallyConstructableType != this) + { + dependencies.Add(maximallyConstructableType, "Type is template-loadable"); + } + } + if (EmitVirtualSlots) { if (!_type.IsArrayTypeWithoutGenericInterfaces()) diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index acad3d3934194a..21889f70eab085 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -66,6 +66,7 @@ private static int Main() TestGenericMethodOnGenericType.Run(); TestIsValueTypeWithoutTypeHandle.Run(); TestMdArrayLoad.Run(); + TestMdArrayLoad2.Run(); TestByRefTypeLoad.Run(); TestGenericLdtoken.Run(); TestAbstractGenericLdtoken.Run(); @@ -2443,6 +2444,21 @@ public static void Run() } } + class TestMdArrayLoad2 + { + class Atom { } + + public static object MakeMdArray() => new T[1,1,1]; + + public static void Run() + { + var mi = typeof(TestMdArrayLoad2).GetMethod(nameof(MakeMdArray)).MakeGenericMethod(GetAtom()); + if (mi.Invoke(null, Array.Empty()) is not Atom[,,]) + throw new Exception(); + static Type GetAtom() => typeof(Atom); + } + } + class TestByRefTypeLoad { class Atom { }