diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs index 87a2c829c0269..36ec3d8b265af 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeMetadataNode.cs @@ -130,6 +130,18 @@ public static void GetMetadataDependencies(ref DependencyList dependencies, Node default: Debug.Assert(type.IsDefType); + // We generally postpone creating MethodTables until absolutely needed. + // IDynamicInterfaceCastableImplementation is special in the sense that just obtaining a System.Type + // (by e.g. browsing custom attribute metadata) gives the user enough to pass this to runtime APIs + // that need a MethodTable. We don't have a legitimate type handle without the MethodTable. Other + // kinds of APIs that expect a MethodTable have enough dataflow annotation to trigger warnings. + // There's no dataflow annotations on the IDynamicInterfaceCastable.GetInterfaceImplementation API. + if (type.IsInterface && ((MetadataType)type).IsDynamicInterfaceCastableImplementation()) + { + dependencies ??= new DependencyList(); + dependencies.Add(nodeFactory.ReflectedType(type), "Reflected IDynamicInterfaceCastableImplementation"); + } + TypeDesc typeDefinition = type.GetTypeDefinition(); if (typeDefinition != type) { diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs index b5023e8c84163..ee8f371b35abf 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs @@ -4,6 +4,7 @@ using System; using System.Text; using System.Collections.Generic; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis; @@ -57,6 +58,7 @@ public static int Run() TestDefaultDynamicStaticNonGeneric.Run(); TestDefaultDynamicStaticGeneric.Run(); TestDynamicStaticGenericVirtualMethods.Run(); + TestRuntime109496Regression.Run(); return Pass; } @@ -1703,4 +1705,57 @@ public static void Run() Console.WriteLine(s_entry.Enter1>("One")); } } + + class TestRuntime109496Regression + { + class CastableThing : IDynamicInterfaceCastable + { + RuntimeTypeHandle IDynamicInterfaceCastable.GetInterfaceImplementation(RuntimeTypeHandle interfaceType) + => Type.GetTypeFromHandle(interfaceType).GetCustomAttribute().TheType.TypeHandle; + bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) + => Type.GetTypeFromHandle(interfaceType).IsDefined(typeof(TypeAttribute)); + } + + [Type(typeof(IMyInterfaceImpl))] + interface IMyInterface + { + int Method(); + } + + [DynamicInterfaceCastableImplementation] + interface IMyInterfaceImpl : IMyInterface + { + int IMyInterface.Method() => 42; + } + + [Type(typeof(IMyGenericInterfaceImpl))] + interface IMyGenericInterface + { + int Method(); + } + + [DynamicInterfaceCastableImplementation] + interface IMyGenericInterfaceImpl : IMyGenericInterface + { + int IMyGenericInterface.Method() => typeof(T).Name.Length; + } + + class TypeAttribute : Attribute + { + public Type TheType { get; } + + public TypeAttribute(Type t) => TheType = t; + } + + public static void Run() + { + object o = new CastableThing(); + + if (((IMyInterface)o).Method() != 42) + throw new Exception(); + + if (((IMyGenericInterface)o).Method() != 5) + throw new Exception(); + } + } }