diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ConstructedTypeRewritingHelpers.cs b/src/coreclr/tools/Common/TypeSystem/Common/ConstructedTypeRewritingHelpers.cs index 41e119bae681d3..3019606023a15c 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ConstructedTypeRewritingHelpers.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ConstructedTypeRewritingHelpers.cs @@ -78,7 +78,6 @@ public static TypeDesc ReplaceTypesInConstructionOfType(this TypeDesc type, Type if (type.HasInstantiation) { TypeDesc[] newInstantiation = null; - Debug.Assert(type is InstantiatedType); int instantiationIndex = 0; for (; instantiationIndex < type.Instantiation.Length; instantiationIndex++) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs index c2132ce7aa6d1f..0f8683d6623b91 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs @@ -30,6 +30,7 @@ public class ReflectionMarker public NodeFactory Factory { get; } public FlowAnnotations Annotations { get; } public DependencyList Dependencies { get => _dependencies; } + public List RuntimeDeterminedDependencies { get; } = new List(); internal enum AccessKind { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index d3e41cd36eaf96..8197802b44b76c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -2,11 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Metadata; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; using ILCompiler.Logging; using ILLink.Shared; using ILLink.Shared.TrimAnalysis; @@ -121,12 +124,13 @@ protected override void Scan(MethodIL methodBody, ref InterproceduralState inter } } - public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, FlowAnnotations annotations, Logger logger, MethodIL methodBody) + public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, FlowAnnotations annotations, Logger logger, MethodIL methodBody, out List runtimeDependencies) { var scanner = new ReflectionMethodBodyScanner(factory, annotations, logger, new MessageOrigin(methodBody.OwningMethod)); scanner.InterproceduralScan(methodBody); + runtimeDependencies = scanner._reflectionMarker.RuntimeDeterminedDependencies; return scanner._reflectionMarker.Dependencies; } @@ -357,8 +361,6 @@ public static bool HandleCall( case IntrinsicId.Type_GetConstructor: case IntrinsicId.MethodBase_GetMethodFromHandle: case IntrinsicId.MethodBase_get_MethodHandle: - case IntrinsicId.Type_MakeGenericType: - case IntrinsicId.MethodInfo_MakeGenericMethod: case IntrinsicId.Expression_Call: case IntrinsicId.Expression_New: case IntrinsicId.Type_GetType: @@ -371,18 +373,124 @@ public static bool HandleCall( case IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: case IntrinsicId.Assembly_CreateInstance: { - bool result = handleCallAction.Invoke(calledMethod, instanceValue, argumentValues, intrinsicId, out methodReturnValue); + return handleCallAction.Invoke(calledMethod, instanceValue, argumentValues, intrinsicId, out methodReturnValue); + } - // Special case some intrinsics for AOT handling (on top of the trimming handling done in the HandleCallAction) - switch (intrinsicId) + case IntrinsicId.Type_MakeGenericType: + { + bool triggersWarning = false; + + if (!instanceValue.IsEmpty() && !argumentValues[0].IsEmpty()) { - case IntrinsicId.Type_MakeGenericType: - case IntrinsicId.MethodInfo_MakeGenericMethod: - CheckAndReportRequires(diagnosticContext, calledMethod, DiagnosticUtilities.RequiresDynamicCodeAttribute); - break; + foreach (var value in instanceValue.AsEnumerable()) + { + if (value is SystemTypeValue typeValue) + { + TypeDesc typeInstantiated = typeValue.RepresentedType.Type; + if (!typeInstantiated.IsGenericDefinition) + { + // Nothing to do, will fail at runtime + } + else if (TryGetMakeGenericInstantiation(callingMethodDefinition, argumentValues[0], out Instantiation inst, out bool isExact)) + { + if (inst.Length == typeInstantiated.Instantiation.Length) + { + typeInstantiated = ((MetadataType)typeInstantiated).MakeInstantiatedType(inst); + + if (isExact) + { + reflectionMarker.MarkType(diagnosticContext.Origin, typeInstantiated, "MakeGenericType"); + } + else + { + reflectionMarker.RuntimeDeterminedDependencies.Add(new MakeGenericTypeSite(typeInstantiated)); + } + } + } + else + { + triggersWarning = true; + } + + } + else if (value == NullValue.Instance) + { + // Nothing to do + } + else + { + // We don't know what type the `MakeGenericType` was called on + triggersWarning = true; + } + } + } + + if (triggersWarning) + { + CheckAndReportRequires(diagnosticContext, calledMethod, DiagnosticUtilities.RequiresDynamicCodeAttribute); + } + + // This intrinsic is relevant to both trimming and AOT - call into trimming logic as well. + return handleCallAction.Invoke(calledMethod, instanceValue, argumentValues, intrinsicId, out methodReturnValue); + } + + case IntrinsicId.MethodInfo_MakeGenericMethod: + { + bool triggersWarning = false; + + if (!instanceValue.IsEmpty()) + { + foreach (var methodValue in instanceValue.AsEnumerable()) + { + if (methodValue is SystemReflectionMethodBaseValue methodBaseValue) + { + MethodDesc methodInstantiated = methodBaseValue.RepresentedMethod.Method; + if (!methodInstantiated.IsGenericMethodDefinition) + { + // Nothing to do, will fail at runtime + } + else if (!methodInstantiated.OwningType.IsGenericDefinition + && TryGetMakeGenericInstantiation(callingMethodDefinition, argumentValues[0], out Instantiation inst, out bool isExact)) + { + if (inst.Length == methodInstantiated.Instantiation.Length) + { + methodInstantiated = methodInstantiated.MakeInstantiatedMethod(inst); + + if (isExact) + { + reflectionMarker.MarkMethod(diagnosticContext.Origin, methodInstantiated, "MakeGenericMethod"); + } + else + { + reflectionMarker.RuntimeDeterminedDependencies.Add(new MakeGenericMethodSite(methodInstantiated)); + } + } + } + else + { + // If the owning type is a generic definition, we can't help much. + triggersWarning = true; + } + } + else if (methodValue == NullValue.Instance) + { + // Nothing to do + } + else + { + // We don't know what method the `MakeGenericMethod` was called on + triggersWarning = true; + } + } } - return result; + if (triggersWarning) + { + CheckAndReportRequires(diagnosticContext, calledMethod, DiagnosticUtilities.RequiresDynamicCodeAttribute); + } + + // This intrinsic is relevant to both trimming and AOT - call into trimming logic as well. + return handleCallAction.Invoke(calledMethod, instanceValue, argumentValues, intrinsicId, out methodReturnValue); } case IntrinsicId.None: @@ -686,6 +794,105 @@ void AddReturnValue(MultiValue value) } } + private static bool TryGetMakeGenericInstantiation( + MethodDesc contextMethod, + in MultiValue genericParametersArray, + out Instantiation inst, + out bool isExact) + { + // We support calling MakeGeneric APIs with a very concrete instantiation array. + // Only the form of `new Type[] { typeof(Foo), typeof(T), typeof(Foo) }` is supported. + + inst = default; + isExact = true; + Debug.Assert(contextMethod.GetTypicalMethodDefinition() == contextMethod); + + var typesValue = genericParametersArray.AsSingleValue(); + if (typesValue is NullValue) + { + // This will fail at runtime but no warning needed + inst = Instantiation.Empty; + return true; + } + + // Is this an array we model? + if (typesValue is not ArrayValue array) + { + return false; + } + + int? size = array.Size.AsConstInt(); + if (size == null) + { + return false; + } + + TypeDesc[]? sigInst = null; + TypeDesc[]? defInst = null; + + ArrayBuilder result = default; + for (int i = 0; i < size.Value; i++) + { + // Go over each element of the array. If the value is unknown, bail. + if (!array.TryGetValueByIndex(i, out MultiValue value)) + { + return false; + } + + var singleValue = value.AsSingleValue(); + + TypeDesc? type = singleValue switch + { + SystemTypeValue systemType => systemType.RepresentedType.Type, + GenericParameterValue genericParamType => genericParamType.GenericParameter.GenericParameter, + NullableSystemTypeValue nullableSystemType => nullableSystemType.NullableType.Type, + _ => null + }; + + if (type is null) + { + return false; + } + + // type is now some type. + // Because dataflow analysis oddly operates on method bodies instantiated over + // generic parameters (as opposed to instantiated over signature variables) + // We need to swap generic parameters (T, U,...) for signature variables (!0, !!1,...). + // We need to do this for both generic parameters of the owning type, and generic + // parameters of the owning method. + if (type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) + { + if (sigInst == null) + { + TypeDesc contextType = contextMethod.OwningType; + sigInst = new TypeDesc[contextType.Instantiation.Length + contextMethod.Instantiation.Length]; + defInst = new TypeDesc[contextType.Instantiation.Length + contextMethod.Instantiation.Length]; + TypeSystemContext context = type.Context; + for (int j = 0; j < contextType.Instantiation.Length; j++) + { + sigInst[j] = context.GetSignatureVariable(j, method: false); + defInst[j] = contextType.Instantiation[j]; + } + for (int j = 0; j < contextMethod.Instantiation.Length; j++) + { + sigInst[j + contextType.Instantiation.Length] = context.GetSignatureVariable(j, method: true); + defInst[j + contextType.Instantiation.Length] = contextMethod.Instantiation[j]; + } + } + + isExact = false; + + // defInst is [T, U, V], sigInst is `[!0, !!0, !!1]`. + type = type.ReplaceTypesInConstructionOfType(defInst, sigInst); + } + + result.Add(type); + } + + inst = new Instantiation(result.ToArray()); + return true; + } + private static bool IsAotUnsafeDelegate(TypeDesc parameterType) { TypeSystemContext context = parameterType.Context; @@ -846,5 +1053,33 @@ private static bool IsPInvokeDangerous(MethodDesc calledMethod, out bool comDang return aotUnsafeDelegate || comDangerousMethod; } + + private sealed class MakeGenericMethodSite : INodeWithRuntimeDeterminedDependencies + { + private readonly MethodDesc _method; + + public MakeGenericMethodSite(MethodDesc method) => _method = method; + + public IEnumerable.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) + { + var list = new DependencyList(); + RootingHelpers.TryGetDependenciesForReflectedMethod(ref list, factory, _method.InstantiateSignature(typeInstantiation, methodInstantiation), "MakeGenericMethod"); + return list; + } + } + + private sealed class MakeGenericTypeSite : INodeWithRuntimeDeterminedDependencies + { + private readonly TypeDesc _type; + + public MakeGenericTypeSite(TypeDesc type) => _type = type; + + public IEnumerable.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation) + { + var list = new DependencyList(); + RootingHelpers.TryGetDependenciesForReflectedType(ref list, factory, _type.InstantiateSignature(typeInstantiation, methodInstantiation), "MakeGenericType"); + return list; + } + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs index 1b73665245936e..6a6e2e0b9fb697 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs @@ -19,6 +19,7 @@ namespace ILCompiler.DependencyAnalysis public class DataflowAnalyzedMethodNode : DependencyNodeCore { private readonly MethodIL _methodIL; + private List _runtimeDependencies; public DataflowAnalyzedMethodNode(MethodIL methodIL) { @@ -32,26 +33,51 @@ public override IEnumerable GetStaticDependencies(NodeFacto var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; try { - return Dataflow.ReflectionMethodBodyScanner.ScanAndProcessReturnValue(factory, mdManager.FlowAnnotations, mdManager.Logger, _methodIL); + return Dataflow.ReflectionMethodBodyScanner.ScanAndProcessReturnValue(factory, mdManager.FlowAnnotations, mdManager.Logger, _methodIL, out _runtimeDependencies); } catch (TypeSystemException) { // Something wrong with the input - missing references, etc. // The method body likely won't compile either, so we don't care. + _runtimeDependencies = new List(); return Array.Empty(); } } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) + { + MethodDesc analyzedMethod = _methodIL.OwningMethod; + + // Look for any generic specialization of this method. If any are found, specialize any dataflow dependencies. + for (int i = firstNode; i < markedNodes.Count; i++) + { + if (markedNodes[i] is not IMethodBodyNode methodBody) + continue; + + MethodDesc method = methodBody.Method; + if (method.GetTypicalMethodDefinition() != analyzedMethod) + continue; + + // Instantiate all runtime dependencies for the found generic specialization + foreach (var n in _runtimeDependencies) + { + foreach (var d in n.InstantiateDependencies(factory, method.OwningType.Instantiation, method.Instantiation)) + { + yield return new CombinedDependencyListEntry(d.Node, null, d.Reason); + } + } + } + } + protected override string GetName(NodeFactory factory) { return "Dataflow analysis for " + _methodIL.OwningMethod.ToString(); } public override bool InterestingForDynamicDependencyAnalysis => false; - public override bool HasDynamicDependencies => false; + public override bool HasDynamicDependencies => _runtimeDependencies.Count > 0; public override bool HasConditionalStaticDependencies => false; public override bool StaticDependenciesAreComputed => true; public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedMethodNode.cs index b4391164b99dff..c72491f1307285 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedMethodNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ScannedMethodNode.cs @@ -82,7 +82,7 @@ public override IEnumerable GetConditionalStaticDep } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; - public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool InterestingForDynamicDependencyAnalysis => _method.HasInstantiation || _method.OwningType.HasInstantiation; public override bool HasDynamicDependencies => false; public override bool HasConditionalStaticDependencies => CodeBasedDependencyAlgorithm.HasConditionalDependenciesDueToMethodCodePresence(_method); diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs index d835084925619c..2d009d6d360364 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -58,6 +58,8 @@ public override ObjectNodeSection GetSection(NodeFactory factory) public override bool StaticDependenciesAreComputed => _methodCode != null; + public override bool InterestingForDynamicDependencyAnalysis => _method.HasInstantiation || _method.OwningType.HasInstantiation; + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.GetMangledMethodName(_method)); diff --git a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs index d08e87bbaa76e3..95e7d071e33430 100644 --- a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs +++ b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs @@ -1051,11 +1051,13 @@ public static void Run() int val = AccessCookie(); Assert.AreEqual(42, val); - val = (int)typeof(ClassWithTemplate<>).MakeGenericType(typeof(C2)).GetField("Cookie").GetValue(null); + val = (int)typeof(ClassWithTemplate<>).MakeGenericType(GetC2()).GetField("Cookie").GetValue(null); Assert.AreEqual(42, val); + static Type GetC2() => typeof(C2); - val = (int)typeof(TestSharedCode).GetMethod(nameof(AccessCookie)).MakeGenericMethod(typeof(C3)).Invoke(null, Array.Empty()); + val = (int)typeof(TestSharedCode).GetMethod(nameof(AccessCookie)).MakeGenericMethod(GetC3()).Invoke(null, Array.Empty()); Assert.AreEqual(42, val); + static Type GetC3() => typeof(C3); } { @@ -1063,13 +1065,15 @@ public static void Run() object val = AccessArray(); Assert.AreEqual(int.MaxValue, GC.GetGeneration(val)); - val = typeof(ClassWithTemplate<>).MakeGenericType(typeof(C4)).GetField("Array").GetValue(null); + val = typeof(ClassWithTemplate<>).MakeGenericType(GetC4()).GetField("Array").GetValue(null); Assert.AreEqual(0, GC.GetGeneration(val)); Assert.AreEqual(nameof(C4), val.GetType().GetElementType().Name); + static Type GetC4() => typeof(C4); - val = typeof(TestSharedCode).GetMethod(nameof(AccessArray)).MakeGenericMethod(typeof(C5)).Invoke(null, Array.Empty()); + val = typeof(TestSharedCode).GetMethod(nameof(AccessArray)).MakeGenericMethod(GetC5()).Invoke(null, Array.Empty()); Assert.AreEqual(0, GC.GetGeneration(val)); Assert.AreEqual(nameof(C5), val.GetType().GetElementType().Name); + static Type GetC5() => typeof(C5); } } } diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index 5abac662bfe7ad..27f332cbdd14c6 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -1824,9 +1824,10 @@ public static void Run() typeof(GenericType<>).MakeGenericType(typeof(object)).GetMethod("Gimme"); } - var t = (Type)s_type.MakeGenericType(typeof(double)).GetMethod("Gimme").Invoke(null, Array.Empty()); + var t = (Type)s_type.MakeGenericType(GetDouble()).GetMethod("Gimme").Invoke(null, Array.Empty()); if (t != typeof(double)) throw new Exception(); + static Type GetDouble() => typeof(double); } } @@ -2432,9 +2433,10 @@ class Atom { } public static void Run() { - var mi = typeof(TestMdArrayLoad).GetMethod(nameof(MakeMdArray)).MakeGenericMethod(typeof(Atom)); + var mi = typeof(TestMdArrayLoad).GetMethod(nameof(MakeMdArray)).MakeGenericMethod(GetAtom()); if ((Type)mi.Invoke(null, Array.Empty()) != typeof(Atom[,,])) throw new Exception(); + static Type GetAtom() => typeof(Atom); } } @@ -2446,9 +2448,10 @@ class Atom { } public static void Run() { - var mi = typeof(TestByRefTypeLoad).GetMethod(nameof(MakeFnPtrType)).MakeGenericMethod(typeof(Atom)); + var mi = typeof(TestByRefTypeLoad).GetMethod(nameof(MakeFnPtrType)).MakeGenericMethod(GetAtom()); if ((Type)mi.Invoke(null, Array.Empty()) != typeof(delegate*)) throw new Exception(); + static Type GetAtom() => typeof(Atom); } } diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs index 5efcd99d9ca643..a75ca2bcd0c4f0 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs @@ -26,6 +26,8 @@ public static int Run() TestDynamicDependency.Run(); TestDynamicDependencyWithGenerics.Run(); TestObjectGetTypeDataflow.Run(); + TestMakeGenericDataflow.Run(); + TestMakeGenericDataflowInvalid.Run(); TestMarshalIntrinsics.Run(); Regression97758.Run(); @@ -588,6 +590,117 @@ public static void Run() } } + class TestMakeGenericDataflow + { + class Gen1 + { + public static void Bridge() { } + } + + class Gen1 + { + public static void Bridge() { } + } + + + class Gen2 + { + public static void Bridge() { } + } + + class Gen2 + { + public static void Bridge() { } + } + + struct MyStruct { } + + static void DoBridgeT1() => typeof(Gen1<,>).MakeGenericType([typeof(T), typeof(U)]).GetMethod(nameof(Gen1.Bridge)).Invoke(null, []); + + static void DoBridgeT2() => typeof(Gen2<>).MakeGenericType([typeof(MyStruct)]).GetMethod(nameof(Gen2.Bridge)).Invoke(null, []); + + static void DoBridgeM1() => typeof(Gen1).GetMethod(nameof(Gen1.Bridge)).MakeGenericMethod([typeof(T), typeof(U)]).Invoke(null, []); + + static void DoBridgeM2() => typeof(Gen2).GetMethod(nameof(Gen2.Bridge)).MakeGenericMethod([typeof(MyStruct)]).Invoke(null, []); + + public static void Run() + { + DoBridgeT1(); + DoBridgeT1(); + DoBridgeT1(); + + DoBridgeT2(); + DoBridgeT2(); + + DoBridgeM1(); + DoBridgeM1(); + DoBridgeM1(); + + DoBridgeM2(); + DoBridgeM2(); + + typeof(Gen1<,>).MakeGenericType([typeof(float), typeof(string)]).GetMethod(nameof(Gen1.Bridge)).Invoke(null, []); + typeof(Gen2<>).MakeGenericType([typeof(MyStruct)]).GetMethod(nameof(Gen2.Bridge)).Invoke(null, []); + typeof(Gen1).GetMethod(nameof(Gen1.Bridge)).MakeGenericMethod([typeof(float), typeof(string)]).Invoke(null, []); + typeof(Gen2).GetMethod(nameof(Gen2.Bridge)).MakeGenericMethod([typeof(MyStruct)]).Invoke(null, []); + } + } + + class TestMakeGenericDataflowInvalid + { + class Gen { } + + class Gen + { + public static void Bridge() { } + } + + public static void Run() + { + try + { + typeof(Gen<>).MakeGenericType(null); + } + catch (ArgumentException) { } + + try + { + typeof(Gen<>).MakeGenericType([]); + } + catch (ArgumentException) { } + + try + { + typeof(Gen<>).MakeGenericType([typeof(float), typeof(double)]); + } + catch (ArgumentException) { } + + try + { + typeof(Gen<>).MakeGenericType([typeof(Gen<>)]); + } + catch (ArgumentException) { } + + try + { + typeof(Gen).GetMethod("Bridge").MakeGenericMethod(null); + } + catch (ArgumentException) { } + + try + { + typeof(Gen).GetMethod("Bridge").MakeGenericMethod([]); + } + catch (ArgumentException) { } + + try + { + typeof(Gen).GetMethod("Bridge").MakeGenericMethod([typeof(float), typeof(double)]); + } + catch (ArgumentException) { } + } + } + class TestMarshalIntrinsics { [StructLayout(LayoutKind.Sequential)] diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs index f2c70d7b1ee60a..c69c013150941b 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs @@ -111,7 +111,8 @@ public static void Run() TestIntf1((IIntf1)new Intf1CastableImpl(), 456); TestIntf2(new Intf2Impl1(), 123); - TestIntf2((IIntf2)Activator.CreateInstance(typeof(Intf2Impl2<>).MakeGenericType(typeof(object))), 456); + TestIntf2((IIntf2)Activator.CreateInstance(typeof(Intf2Impl2<>).MakeGenericType(GetObject())), 456); + static Type GetObject() => typeof(object); } } diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs index 145d7b3d0c3ea9..a9d3d44c758dde 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs @@ -1976,8 +1976,10 @@ private static void TestDynamicStaticFields() Foo.s_floatField = 12.34f; Foo.s_longField1 = 0x1111; - var fooDynamicOfClassType = typeof(Foo<>).MakeGenericType(typeof(ClassType)).GetTypeInfo(); - var fooDynamicOfClassType2 = typeof(Foo<>).MakeGenericType(typeof(ClassType2)).GetTypeInfo(); + var fooDynamicOfClassType = typeof(Foo<>).MakeGenericType(GetClassType()).GetTypeInfo(); + static Type GetClassType() => typeof(ClassType); + var fooDynamicOfClassType2 = typeof(Foo<>).MakeGenericType(GetClassType2()).GetTypeInfo(); + static Type GetClassType2() => typeof(ClassType2); FieldInfo fi = fooDynamicOfClassType.GetDeclaredField("s_intField"); FieldInfo fi2 = fooDynamicOfClassType2.GetDeclaredField("s_intField"); @@ -2031,7 +2033,8 @@ private static void TestDynamicInvokeStubs() heh2.GenericVirtualMethod(new Program(), "ayy"); // Simple method invocation - var dynamicBaseOfString = typeof(DynamicBase<>).MakeGenericType(typeof(string)); + var dynamicBaseOfString = typeof(DynamicBase<>).MakeGenericType(GetString()); + static Type GetString() => typeof(string); object obj = Activator.CreateInstance(dynamicBaseOfString); { var simpleMethod = dynamicBaseOfString.GetTypeInfo().GetDeclaredMethod("SimpleMethod"); @@ -2054,7 +2057,7 @@ private static void TestDynamicInvokeStubs() } { - var dynamicDerivedOfString = typeof(DynamicDerived<>).MakeGenericType(typeof(string)); + var dynamicDerivedOfString = typeof(DynamicDerived<>).MakeGenericType(GetString()); object dynamicDerivedObj = Activator.CreateInstance(dynamicDerivedOfString); var virtualMethodDynamicDerived = dynamicDerivedOfString.GetTypeInfo().GetDeclaredMethod("VirtualMethod"); string result = (string)virtualMethodDynamicDerived.Invoke(dynamicDerivedObj, new[] { "fad" }); @@ -2063,7 +2066,7 @@ private static void TestDynamicInvokeStubs() // Test generic method invocation { - var genericMethod = dynamicBaseOfString.GetTypeInfo().GetDeclaredMethod("GenericMethod").MakeGenericMethod(new[] { typeof(string) }); + var genericMethod = dynamicBaseOfString.GetTypeInfo().GetDeclaredMethod("GenericMethod").MakeGenericMethod(new[] { GetString() }); string result = (string)genericMethod.Invoke(obj, new[] { "hey", "hello" }); Verify("System.Stringhello", result); @@ -2072,15 +2075,15 @@ private static void TestDynamicInvokeStubs() // Test GVM invocation { var genericMethod = dynamicBaseOfString.GetTypeInfo().GetDeclaredMethod("GenericVirtualMethod"); - genericMethod = genericMethod.MakeGenericMethod(new[] { typeof(string) }); + genericMethod = genericMethod.MakeGenericMethod(new[] { GetString() }); string result = (string)genericMethod.Invoke(obj, new[] { "hey", "hello" }); Verify("DynamicBaseSystem.Stringhello", result); } { - var dynamicDerivedOfString = typeof(DynamicDerived<>).MakeGenericType(typeof(string)); + var dynamicDerivedOfString = typeof(DynamicDerived<>).MakeGenericType(GetString()); object dynamicDerivedObj = Activator.CreateInstance(dynamicDerivedOfString); - var virtualMethodDynamicDerived = dynamicDerivedOfString.GetTypeInfo().GetDeclaredMethod("GenericVirtualMethod").MakeGenericMethod(new[] { typeof(string) }); + var virtualMethodDynamicDerived = dynamicDerivedOfString.GetTypeInfo().GetDeclaredMethod("GenericVirtualMethod").MakeGenericMethod(new[] { GetString() }); string result = (string)virtualMethodDynamicDerived.Invoke(dynamicDerivedObj, new[] { "hey", "fad" }); Verify("DynamicDerivedSystem.Stringfad", result); } diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs index 4f34959be84b3b..b5023e8c84163e 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs @@ -1583,11 +1583,13 @@ class Gen where T : IFoo [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { - var r = (string)typeof(Gen<>).MakeGenericType(typeof(Baz)).GetMethod("GrabCookie").Invoke(null, Array.Empty()); + var r = (string)typeof(Gen<>).MakeGenericType(GetBaz()).GetMethod("GrabCookie").Invoke(null, Array.Empty()); + static Type GetBaz() => typeof(Baz); if (r != "IBar") throw new Exception(r); - r = (string)typeof(Gen<>).MakeGenericType(typeof(IBar)).GetMethod("GrabCookie").Invoke(null, Array.Empty()); + r = (string)typeof(Gen<>).MakeGenericType(GetIBar()).GetMethod("GrabCookie").Invoke(null, Array.Empty()); + static Type GetIBar() => typeof(IBar); if (r != "IBar") throw new Exception(r); } @@ -1620,15 +1622,18 @@ class Gen where T : IFoo [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType - Intentional")] public static void Run() { - Activator.CreateInstance(typeof(Baz<>).MakeGenericType(typeof(Atom1))); + Activator.CreateInstance(typeof(Baz<>).MakeGenericType(GetAtom1())); - var r = (string)typeof(Gen<>).MakeGenericType(typeof(Baz<>).MakeGenericType(typeof(Atom1))).GetMethod("GrabCookie").Invoke(null, Array.Empty()); + var r = (string)typeof(Gen<>).MakeGenericType(typeof(Baz<>).MakeGenericType(GetAtom1())).GetMethod("GrabCookie").Invoke(null, Array.Empty()); if (r != "IBar") throw new Exception(r); - r = (string)typeof(Gen<>).MakeGenericType(typeof(IBar<>).MakeGenericType(typeof(Atom2))).GetMethod("GrabCookie").Invoke(null, Array.Empty()); + r = (string)typeof(Gen<>).MakeGenericType(typeof(IBar<>).MakeGenericType(GetAtom2())).GetMethod("GrabCookie").Invoke(null, Array.Empty()); if (r != "IBar") throw new Exception(r); + + static Type GetAtom1() => typeof(Atom1); + static Type GetAtom2() => typeof(Atom2); } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs index 55d07efff70c16..2f749beae0f813 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ExponentialDataFlow.cs @@ -58,7 +58,7 @@ class GenericTypeWithRequires<[DynamicallyAccessedMembers (DynamicallyAccessedMe { } - [ExpectedWarning ("IL3050", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.Analyzer)] [ExpectedWarning ("IL2090", "'T'")] public static void Test () {