diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index c55d468a4d4c8e..f93c101788f5ea 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -547,6 +547,11 @@ public static unsafe IntPtr ResolveStaticDispatchOnType(RuntimeTypeHandle instan return result; } + public static unsafe IntPtr ResolveDispatchOnType(RuntimeTypeHandle instanceType, RuntimeTypeHandle interfaceType, int slot) + { + return RuntimeImports.RhResolveDispatchOnType(instanceType.ToMethodTable(), interfaceType.ToMethodTable(), checked((ushort)slot)); + } + public static bool IsUnmanagedPointerType(RuntimeTypeHandle typeHandle) { return typeHandle.ToMethodTable()->IsPointer; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs index eb911d4037afde..c630754b50bb5f 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionaryCell.cs @@ -140,10 +140,41 @@ internal override IntPtr Create(TypeBuilder builder) } } + /// + /// Used for non-generic instance constrained Methods + /// + private class NonGenericInstanceConstrainedMethodCell : GenericDictionaryCell + { + internal TypeDesc ConstraintType; + internal TypeDesc ConstrainedMethodType; + internal int ConstrainedMethodSlot; + + internal override void Prepare(TypeBuilder builder) + { + if (ConstraintType.IsCanonicalSubtype(CanonicalFormKind.Any) || ConstrainedMethodType.IsCanonicalSubtype(CanonicalFormKind.Any)) + Environment.FailFast("Unable to compute call information for a canonical type/method."); + + builder.RegisterForPreparation(ConstraintType); + builder.RegisterForPreparation(ConstrainedMethodType); + } + + internal override IntPtr Create(TypeBuilder builder) + { + IntPtr result = RuntimeAugments.ResolveDispatchOnType( + builder.GetRuntimeTypeHandle(ConstraintType), + builder.GetRuntimeTypeHandle(ConstrainedMethodType), + ConstrainedMethodSlot); + + Debug.Assert(result != IntPtr.Zero); + + return result; + } + } + /// /// Used for generic static constrained Methods /// - private class GenericStaticConstrainedMethodCell : GenericDictionaryCell + private class GenericConstrainedMethodCell : GenericDictionaryCell { internal DefType ConstraintType; internal InstantiatedMethod ConstrainedMethod; @@ -512,29 +543,45 @@ internal static GenericDictionaryCell ParseAndCreateCell(NativeLayoutInfoLoadCon break; case FixupSignatureKind.NonGenericStaticConstrainedMethod: - { + case FixupSignatureKind.NonGenericInstanceConstrainedMethod: + { var constraintType = nativeLayoutInfoLoadContext.GetType(ref parser); var constrainedMethodType = nativeLayoutInfoLoadContext.GetType(ref parser); var constrainedMethodSlot = parser.GetUnsigned(); - TypeLoaderLogger.WriteLine("NonGenericStaticConstrainedMethod: " + constraintType.ToString() + " Method " + constrainedMethodType.ToString() + ", slot #" + constrainedMethodSlot.LowLevelToString()); - cell = new NonGenericStaticConstrainedMethodCell() + string kindString = kind == FixupSignatureKind.NonGenericStaticConstrainedMethod ? "NonGenericStaticConstrainedMethod: " : "NonGenericInstanceConstrainedMethod: "; + + TypeLoaderLogger.WriteLine(kindString + constraintType.ToString() + " Method " + constrainedMethodType.ToString() + ", slot #" + constrainedMethodSlot.LowLevelToString()); + + if (kind == FixupSignatureKind.NonGenericStaticConstrainedMethod) { - ConstraintType = constraintType, - ConstrainedMethodType = constrainedMethodType, - ConstrainedMethodSlot = (int)constrainedMethodSlot - }; + cell = new NonGenericStaticConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethodType = constrainedMethodType, + ConstrainedMethodSlot = (int)constrainedMethodSlot + }; + } + else + { + cell = new NonGenericInstanceConstrainedMethodCell() + { + ConstraintType = constraintType, + ConstrainedMethodType = constrainedMethodType, + ConstrainedMethodSlot = (int)constrainedMethodSlot + }; + } } break; - case FixupSignatureKind.GenericStaticConstrainedMethod: - { + case FixupSignatureKind.GenericConstrainedMethod: + { TypeDesc constraintType = nativeLayoutInfoLoadContext.GetType(ref parser); MethodDesc constrainedMethod = nativeLayoutInfoLoadContext.GetMethod(ref parser); - TypeLoaderLogger.WriteLine("GenericStaticConstrainedMethod: " + constraintType.ToString() + " Method " + constrainedMethod.ToString()); + TypeLoaderLogger.WriteLine("GenericConstrainedMethod: " + constraintType.ToString() + " Method " + constrainedMethod.ToString()); - cell = new GenericStaticConstrainedMethodCell() + cell = new GenericConstrainedMethodCell() { ConstraintType = (DefType)constraintType, ConstrainedMethod = (InstantiatedMethod)constrainedMethod, diff --git a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs index ac69f6872a6b75..9b655d00578dcd 100644 --- a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs +++ b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs @@ -469,7 +469,9 @@ public static MethodDesc TryResolveConstraintMethodApprox(this TypeDesc constrai potentialInterfaceMethod.GetTypicalMethodDefinition(), (InstantiatedType)potentialInterfaceType); } - method = canonType.ResolveInterfaceMethodToVirtualMethodOnType(potentialInterfaceMethod); + method = canonType.ResolveInterfaceMethodToVirtualMethodOnType(potentialInterfaceMethod) + // Do not lose track of `method` if we were able to resolve it previously + ?? method; // See code:#TryResolveConstraintMethodApprox_DoNotReturnParentMethod if (method != null && !method.OwningType.IsValueType) diff --git a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs index dbe629602172b5..7962ee0a15d29a 100644 --- a/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs +++ b/src/coreclr/tools/Common/Internal/NativeFormat/NativeFormat.cs @@ -98,9 +98,9 @@ enum FixupSignatureKind : uint // unused = 0x17, // unused = 0x18, // unused = 0x19, - // unused = 0x20, + NonGenericInstanceConstrainedMethod = 0x20, NonGenericStaticConstrainedMethod = 0x21, - GenericStaticConstrainedMethod = 0x22, + GenericConstrainedMethod = 0x22, NotYetSupported = 0xee, } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs index 5f05fdeb9f4703..f0eae5441d0c28 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs @@ -936,8 +936,6 @@ public override DefaultInterfaceMethodResolution ResolveVariantInterfaceMethodTo public static DefaultInterfaceMethodResolution ResolveVariantInterfaceMethodToDefaultImplementationOnType(MethodDesc interfaceMethod, MetadataType currentType, out MethodDesc impl) { - Debug.Assert(interfaceMethod.Signature.IsStatic); - MetadataType interfaceType = (MetadataType)interfaceMethod.OwningType; bool foundInterface = IsInterfaceImplementedOnType(currentType, interfaceType); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs index 49cf52b524c69a..ac646f21778964 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs @@ -901,30 +901,38 @@ public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultCo TypeDesc instantiatedConstraintType = _constraintType.GetNonRuntimeDeterminedTypeFromRuntimeDeterminedSubtypeViaSubstitution(dictionary.TypeInstantiation, dictionary.MethodInstantiation); MethodDesc implMethod; + MethodDesc instantiatedConstrainedMethodDefinition = instantiatedConstrainedMethod.GetMethodDefinition(); + if (instantiatedConstrainedMethod.OwningType.IsInterface) { if (instantiatedConstrainedMethod.Signature.IsStatic) { - implMethod = instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(instantiatedConstrainedMethod); - if (implMethod == null) - { - DefaultInterfaceMethodResolution resolution = - instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToDefaultImplementationOnType(instantiatedConstrainedMethod, out implMethod); - if (resolution != DefaultInterfaceMethodResolution.DefaultImplementation) - { - // TODO: diamond/reabstraction - ThrowHelper.ThrowInvalidProgramException(); - } - } + implMethod = instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToStaticVirtualMethodOnType(instantiatedConstrainedMethodDefinition); } else { - throw new NotImplementedException(); + implMethod = instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToVirtualMethodOnType(instantiatedConstrainedMethodDefinition); + } + + if (implMethod == null) + { + DefaultInterfaceMethodResolution resolution = + instantiatedConstraintType.GetClosestDefType().ResolveVariantInterfaceMethodToDefaultImplementationOnType(instantiatedConstrainedMethodDefinition, out implMethod); + if (resolution != DefaultInterfaceMethodResolution.DefaultImplementation) + { + // TODO: diamond/reabstraction: https://github.com/dotnet/runtime/issues/72589 + ThrowHelper.ThrowInvalidProgramException(); + } } } else { - implMethod = instantiatedConstraintType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(instantiatedConstrainedMethod); + implMethod = instantiatedConstraintType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(instantiatedConstrainedMethodDefinition); + } + + if (instantiatedConstrainedMethod != instantiatedConstrainedMethodDefinition) + { + implMethod = implMethod.MakeInstantiatedMethod(instantiatedConstrainedMethod.Instantiation); } // AOT use of this generic lookup is restricted to finding methods on valuetypes (runtime usage of this slot in universal generics is more flexible) @@ -933,21 +941,10 @@ public override ISymbolNode GetTarget(NodeFactory factory, GenericLookupResultCo factory.MetadataManager.NoteOverridingMethod(_constrainedMethod, implMethod); // TODO-SIZE: this is address taken only in the delegate target case - if (implMethod.Signature.IsStatic) - { - if (implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).IsSharedByGenericInstantiations) - return factory.ExactCallableAddressTakenAddress(implMethod); - else - return factory.AddressTakenMethodEntrypoint(implMethod); - } - else if (implMethod.HasInstantiation) - { + if (implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).IsSharedByGenericInstantiations) return factory.ExactCallableAddressTakenAddress(implMethod); - } else - { - return factory.AddressTakenMethodEntrypoint(implMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); - } + return factory.AddressTakenMethodEntrypoint(implMethod); } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs index 60b9a82dd0f930..c57f8ad072c233 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutVertexNode.cs @@ -1414,7 +1414,6 @@ public NativeLayoutConstrainedMethodDictionarySlotNode(MethodDesc constrainedMet _directCall = directCall; Debug.Assert(_constrainedMethod.OwningType.IsInterface); Debug.Assert(!_constrainedMethod.HasInstantiation || !directCall); - Debug.Assert(_constrainedMethod.Signature.IsStatic); } protected sealed override string GetName(NodeFactory factory) => @@ -1428,10 +1427,12 @@ protected sealed override FixupSignatureKind SignatureKind { get { - if (_constrainedMethod.HasInstantiation) - return FixupSignatureKind.GenericStaticConstrainedMethod; - else - return FixupSignatureKind.NonGenericStaticConstrainedMethod; + return (_constrainedMethod.HasInstantiation, _constrainedMethod.Signature.IsStatic) switch + { + (true, _) => FixupSignatureKind.GenericConstrainedMethod, + (false, true) => FixupSignatureKind.NonGenericStaticConstrainedMethod, + (false, false) => FixupSignatureKind.NonGenericInstanceConstrainedMethod, + }; } } @@ -1477,13 +1478,13 @@ protected sealed override Vertex WriteSignatureVertex(NativeWriter writer, NodeF Vertex constraintType = factory.NativeLayout.TypeSignatureVertex(_constraintType).WriteVertex(factory); if (_constrainedMethod.HasInstantiation) { - Debug.Assert(SignatureKind is FixupSignatureKind.GenericStaticConstrainedMethod); + Debug.Assert(SignatureKind is FixupSignatureKind.GenericConstrainedMethod); Vertex constrainedMethodVertex = factory.NativeLayout.MethodEntry(_constrainedMethod).WriteVertex(factory); return writer.GetTuple(constraintType, constrainedMethodVertex); } else { - Debug.Assert(SignatureKind is FixupSignatureKind.NonGenericStaticConstrainedMethod); + Debug.Assert(SignatureKind is FixupSignatureKind.NonGenericStaticConstrainedMethod or FixupSignatureKind.NonGenericInstanceConstrainedMethod); Vertex methodType = factory.NativeLayout.TypeSignatureVertex(_constrainedMethod.OwningType).WriteVertex(factory); var canonConstrainedMethod = _constrainedMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); int interfaceSlot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, canonConstrainedMethod, canonConstrainedMethod.OwningType); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 31c9035787ee48..74bf75817ef602 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -550,42 +550,39 @@ private void ImportCall(ILOpcode opcode, int token) bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn; - if (directCall && resolvedConstraint && exactContextNeedsRuntimeLookup) + if (directCall && resolvedConstraint && (exactContextNeedsRuntimeLookup || forceUseRuntimeLookup)) { // We want to do a direct call to a shared method on a valuetype. We need to provide // a generic context, but the JitInterface doesn't provide a way for us to do it from here. // So we do the next best thing and ask RyuJIT to look up a fat pointer. - // - // We have the canonical version of the method - find the runtime determined version. - // This is simplified because we know the method is on a valuetype. - Debug.Assert(targetMethod.OwningType.IsValueType); if (forceUseRuntimeLookup) { - // The below logic would incorrectly resolve the lookup into the first match we found, - // but there was a compile-time ambiguity due to shared code. The correct fix should - // use the ConstrainedMethodUseLookupResult dictionary entry so that the exact - // dispatch can be computed with the help of the generic dictionary. - // We fail the compilation here to avoid bad codegen. This is not actually an invalid program. - // https://github.com/dotnet/runtimelab/issues/1431 - ThrowHelper.ThrowInvalidProgramException(); + var constrainedCallInfo = new ConstrainedCallInfo(_constrained, runtimeDeterminedMethod); + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.ConstrainedDirectCall, constrainedCallInfo), reason); } - - MethodDesc targetOfLookup; - if (_constrained.IsRuntimeDeterminedType) - targetOfLookup = _compilation.TypeSystemContext.GetMethodForRuntimeDeterminedType(targetMethod.GetTypicalMethodDefinition(), (RuntimeDeterminedType)_constrained); - else if (_constrained.HasInstantiation) - targetOfLookup = _compilation.TypeSystemContext.GetMethodForInstantiatedType(targetMethod.GetTypicalMethodDefinition(), (InstantiatedType)_constrained); else - targetOfLookup = targetMethod.GetMethodDefinition(); - if (targetOfLookup.HasInstantiation) { - targetOfLookup = targetOfLookup.MakeInstantiatedMethod(runtimeDeterminedMethod.Instantiation); - } - Debug.Assert(targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); - _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, targetOfLookup), reason); + // We have the canonical version of the method - find the runtime determined version. + // This is simplified because we know the method is on a valuetype. + Debug.Assert(targetMethod.OwningType.IsValueType); + + MethodDesc targetOfLookup; + if (_constrained.IsRuntimeDeterminedType) + targetOfLookup = _compilation.TypeSystemContext.GetMethodForRuntimeDeterminedType(targetMethod.GetTypicalMethodDefinition(), (RuntimeDeterminedType)_constrained); + else if (_constrained.HasInstantiation) + targetOfLookup = _compilation.TypeSystemContext.GetMethodForInstantiatedType(targetMethod.GetTypicalMethodDefinition(), (InstantiatedType)_constrained); + else + targetOfLookup = targetMethod.GetMethodDefinition(); + if (targetOfLookup.HasInstantiation) + { + targetOfLookup = targetOfLookup.MakeInstantiatedMethod(runtimeDeterminedMethod.Instantiation); + } + Debug.Assert(targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); + _dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, targetOfLookup), reason); - targetForDelegate = targetOfLookup; + targetForDelegate = targetOfLookup; + } } else if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg()) { @@ -700,6 +697,7 @@ private void ImportCall(ILOpcode opcode, int token) { Debug.Assert(targetMethod.OwningType.IsInterface && targetMethod.IsVirtual && _constrained != null); + // TODO: https://github.com/dotnet/runtime/issues/72589 ThrowHelper.ThrowBadImageFormatException(); } else if (method.Signature.IsStatic) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 1cd7c8503a192c..d7a6a5a1a74ec5 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -420,7 +420,7 @@ private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetM } else if (staticResolution is DefaultInterfaceMethodResolution.Diamond or DefaultInterfaceMethodResolution.Reabstraction) { - // TODO + // TODO: https://github.com/dotnet/runtime/issues/72589 ThrowHelper.ThrowInvalidProgramException(); } } @@ -1377,7 +1377,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO ThrowHelper.ThrowBadImageFormatException(); } - if (directCall && resolvedConstraint && pResult->exactContextNeedsRuntimeLookup) + if (directCall && resolvedConstraint && (pResult->exactContextNeedsRuntimeLookup || forceUseRuntimeLookup)) { // We want to do a direct call to a shared method on a type. We need to provide // a generic context, but the JitInterface doesn't provide a way for us to do it from here. @@ -1387,39 +1387,43 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO pResult->codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_VALUE; pResult->nullInstanceCheck = !targetMethod.Signature.IsStatic; - // We have the canonical version of the method - find the runtime determined version. - // For now simplify it by assuming the resolved method is on the same type as the constraint. - Debug.Assert(targetMethod.OwningType.GetTypeDefinition() == constrainedType.GetTypeDefinition()); TypeDesc runtimeDeterminedConstrainedType = (TypeDesc)GetRuntimeDeterminedObjectForToken(ref *pConstrainedResolvedToken); + object targetOfLookup; + ReadyToRunHelperId lookupHelper; if (forceUseRuntimeLookup) { - // The below logic would incorrectly resolve the lookup into the first match we found, - // but there was a compile-time ambiguity due to shared code. The correct fix should - // use the ConstrainedMethodUseLookupResult dictionary entry so that the exact - // dispatch can be computed with the help of the generic dictionary. - // We fail the compilation here to avoid bad codegen. This is not actually an invalid program. - // https://github.com/dotnet/runtimelab/issues/1431 - ThrowHelper.ThrowInvalidProgramException(); + MethodDesc runtimeDeterminedInterfaceMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + targetOfLookup = new ConstrainedCallInfo(runtimeDeterminedConstrainedType, runtimeDeterminedInterfaceMethod); + lookupHelper = ReadyToRunHelperId.ConstrainedDirectCall; } - - MethodDesc targetOfLookup; - if (runtimeDeterminedConstrainedType.IsRuntimeDeterminedType) - targetOfLookup = _compilation.TypeSystemContext.GetMethodForRuntimeDeterminedType(targetMethod.GetTypicalMethodDefinition(), (RuntimeDeterminedType)runtimeDeterminedConstrainedType); - else if (runtimeDeterminedConstrainedType.HasInstantiation) - targetOfLookup = _compilation.TypeSystemContext.GetMethodForInstantiatedType(targetMethod.GetTypicalMethodDefinition(), (InstantiatedType)runtimeDeterminedConstrainedType); else - targetOfLookup = targetMethod.GetMethodDefinition(); - if (targetOfLookup.HasInstantiation) { - var methodToGetInstantiation = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); - targetOfLookup = targetOfLookup.MakeInstantiatedMethod(methodToGetInstantiation.Instantiation); + // We have the canonical version of the method - find the runtime determined version. + // For now simplify it by assuming the resolved method is on the same type as the constraint. + Debug.Assert(targetMethod.OwningType.GetTypeDefinition() == constrainedType.GetTypeDefinition()); + + MethodDesc lookupMethod; + if (runtimeDeterminedConstrainedType.IsRuntimeDeterminedType) + lookupMethod = _compilation.TypeSystemContext.GetMethodForRuntimeDeterminedType(targetMethod.GetTypicalMethodDefinition(), (RuntimeDeterminedType)runtimeDeterminedConstrainedType); + else if (runtimeDeterminedConstrainedType.HasInstantiation) + lookupMethod = _compilation.TypeSystemContext.GetMethodForInstantiatedType(targetMethod.GetTypicalMethodDefinition(), (InstantiatedType)runtimeDeterminedConstrainedType); + else + lookupMethod = targetMethod.GetMethodDefinition(); + if (lookupMethod.HasInstantiation) + { + var methodToGetInstantiation = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pResolvedToken); + lookupMethod = lookupMethod.MakeInstantiatedMethod(methodToGetInstantiation.Instantiation); + } + Debug.Assert(lookupMethod.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); + + targetOfLookup = lookupMethod; + lookupHelper = ReadyToRunHelperId.MethodEntry; } - Debug.Assert(targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); ComputeLookup(ref pResolvedToken, targetOfLookup, - ReadyToRunHelperId.MethodEntry, + lookupHelper, HandleToObject(callerHandle), ref pResult->codePointerOrStubLookup); diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2.ilproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2.ilproj index 439059eb517c92..e0fc0740de6f58 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2.ilproj +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2.ilproj @@ -1,4 +1,8 @@ + + + true + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_gm.ilproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_gm.ilproj index c36a90952e1d22..ea1de7ddf0d4fd 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_gm.ilproj +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrained2_gm.ilproj @@ -1,4 +1,8 @@ + + + true + diff --git a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj index 9777726ad29a5d..5cf4faecf03a79 100644 --- a/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj +++ b/src/tests/Loader/classloader/DefaultInterfaceMethods/constrainedcall/constrainedcall.ilproj @@ -1,4 +1,8 @@ + + + true + diff --git a/src/tests/Regressions/coreclr/16354/notimplemented.ilproj b/src/tests/Regressions/coreclr/16354/notimplemented.ilproj index 66165d6484cdb1..6960c9f6da06f6 100644 --- a/src/tests/Regressions/coreclr/16354/notimplemented.ilproj +++ b/src/tests/Regressions/coreclr/16354/notimplemented.ilproj @@ -5,7 +5,9 @@ false - + + + true diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 49ffcb88a3453f..131c11f3341b80 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -965,15 +965,6 @@ https://github.com/dotnet/runtimelab/issues/155: Reflection.Emit - - - - - - - - - diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs index 5521646cd1fb7f..5850a7343e1201 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs @@ -60,6 +60,7 @@ internal static int Run() Test104913Regression.Run(); Test105397Regression.Run(); Test105880Regression.Run(); + TestInterfaceGenericCornerCase.Run(); Test115442Regression.Run(); TestInvokeMemberCornerCaseInGenerics.Run(); TestRefAny.Run(); @@ -3686,6 +3687,85 @@ public static void Run() } } + class TestInterfaceGenericCornerCase + { + interface IFoo + { + string Print(T x); + + string PrintGeneric(T x); + } + + class Conversion where T : IFoo, new() where U : class + { + public string Print() + { + string result = new T().Print(default); + + Func f = new T().Print; + if (f(default) != result) + throw new Exception(); + + return result; + } + + public string PrintGeneric() + { + string result = new T().PrintGeneric(default); + + Func f = new T().PrintGeneric; + if (f(default) != result) + throw new Exception(); + + return result; + } + } + + struct MyStruct : IFoo, IFoo + { + public string Print(Derived x) => "Derived"; + public string Print(object x) => "Object"; + + public string PrintGeneric(Derived x) => $"<{(typeof(U) == typeof(object) ? "O" : "X")}>Derived"; + public string PrintGeneric(object x) => $"<{(typeof(U) == typeof(object) ? "O" : "X")}>Object"; + } + + class Base; + class Derived : Base; + + public static void Run() + { + var conversion = new Conversion(); + if (conversion.Print() != "Object") + throw new Exception(); + if (conversion.PrintGeneric() != "Object") + throw new Exception(); + + if (PrintDynamic(typeof(Derived)) != "Derived") + throw new Exception(); + if (PrintGenericDynamic(typeof(Derived)) != "Derived") + throw new Exception(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static string PrintDynamic(Type p) + { + var t = typeof(Conversion<,>).MakeGenericType(typeof(MyStruct), p); + var g = Activator.CreateInstance(t); + var mi = (MethodInfo)t.GetMemberWithSameMetadataDefinitionAs(typeof(Conversion<,>).GetMethod(nameof(Conversion.Print))); + return (string)mi.Invoke(g, []); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static string PrintGenericDynamic(Type p) + { + var t = typeof(Conversion<,>).MakeGenericType(typeof(MyStruct), p); + var g = Activator.CreateInstance(t); + var mi = ((MethodInfo)t.GetMemberWithSameMetadataDefinitionAs(typeof(Conversion<,>).GetMethod(nameof(Conversion.PrintGeneric)))).MakeGenericMethod(typeof(object)); + return (string)mi.Invoke(g, []); + } + } + class Test115442Regression { public readonly struct TypeBuilder