diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index fdbdfed16b80f6..c3f40ee29ff659 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5475,7 +5475,8 @@ namespace MethodDesc *interfaceMD, MethodTable *interfaceMT, BOOL allowVariance, - MethodDesc **candidateMD) + MethodDesc **candidateMD, + ClassLoadLevel level) { *candidateMD = NULL; @@ -5568,7 +5569,8 @@ namespace candidateMaybe = pMT->TryResolveVirtualStaticMethodOnThisType( interfaceMT, interfaceMD, - /* verifyImplemented */ FALSE); + /* verifyImplemented */ FALSE, + /* level */ level); } } } @@ -5588,9 +5590,11 @@ namespace FALSE, // forceBoxedEntryPoint candidateMaybe->HasMethodInstantiation() ? candidateMaybe->AsInstantiatedMethodDesc()->IMD_GetMethodInstantiation() : - Instantiation(), // for method themselves that are generic + Instantiation(), // for method themselves that are generic FALSE, // allowInstParam - TRUE // forceRemoteableMethod + TRUE, // forceRemoteableMethod + TRUE, // allowCreate + level // level ); } @@ -5607,7 +5611,8 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( MethodTable *pInterfaceMT, MethodDesc **ppDefaultMethod, BOOL allowVariance, - BOOL throwOnConflict + BOOL throwOnConflict, + ClassLoadLevel level ) { CONTRACT(BOOL) { @@ -5627,7 +5632,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( // Check the current method table itself MethodDesc *candidateMaybe = NULL; - if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe)) + if (IsInterface() && TryGetCandidateImplementation(this, pInterfaceMD, pInterfaceMT, allowVariance, &candidateMaybe, level)) { _ASSERTE(candidateMaybe != NULL); @@ -5667,7 +5672,7 @@ BOOL MethodTable::FindDefaultInterfaceImplementation( MethodTable *pCurMT = it.GetInterface(pMT); MethodDesc *pCurMD = NULL; - if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD)) + if (TryGetCandidateImplementation(pCurMT, pInterfaceMD, pInterfaceMT, allowVariance, &pCurMD, level)) { // // Found a match. But is it a more specific match (we want most specific interfaces) @@ -7991,7 +7996,8 @@ MethodTable::ResolveVirtualStaticMethod( BOOL allowNullResult, BOOL verifyImplemented, BOOL allowVariantMatches, - BOOL* uniqueResolution) + BOOL* uniqueResolution, + ClassLoadLevel level) { if (uniqueResolution != nullptr) { @@ -8024,7 +8030,7 @@ MethodTable::ResolveVirtualStaticMethod( // Search for match on a per-level in the type hierarchy for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable()) { - MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented); + MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented, level); if (pMD != nullptr) { return pMD; @@ -8068,7 +8074,7 @@ MethodTable::ResolveVirtualStaticMethod( { // Variant or equivalent matching interface found // Attempt to resolve on variance matched interface - pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented); + pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pItfInMap, pInterfaceMD, verifyImplemented, level); if (pMD != nullptr) { return pMD; @@ -8082,7 +8088,8 @@ MethodTable::ResolveVirtualStaticMethod( pInterfaceType, &pMD, /* allowVariance */ allowVariantMatches, - /* throwOnConflict */ uniqueResolution == nullptr); + /* throwOnConflict */ uniqueResolution == nullptr, + level); if (haveUniqueDefaultImplementation || (pMD != nullptr && (verifyImplemented || uniqueResolution != nullptr))) { // We tolerate conflicts upon verification of implemented SVMs so that they only blow up when actually called at execution time. @@ -8112,7 +8119,7 @@ MethodTable::ResolveVirtualStaticMethod( // Try to locate the appropriate MethodImpl matching a given interface static virtual method. // Returns nullptr on failure. MethodDesc* -MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented) +MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, ClassLoadLevel level) { HRESULT hr = S_OK; IMDInternalImport* pMDInternalImport = GetMDImport(); @@ -8239,7 +8246,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType /* allowInstParam */ FALSE, /* forceRemotableMethod */ FALSE, /* allowCreate */ TRUE, - /* level */ CLASS_LOADED); + /* level */ level); } if (pMethodImpl != nullptr) { @@ -8285,7 +8292,8 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE, - /* uniqueResolution */ &uniqueResolution))) + /* uniqueResolution */ &uniqueResolution, + /* level */ CLASS_LOAD_EXACTPARENTS))) { IMDInternalImport* pInternalImport = GetModule()->GetMDImport(); GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL); diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 92d35b1273651f..c9ebd7a8982424 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2094,13 +2094,15 @@ class MethodTable // Specify allowVariantMatches to permit generic interface variance // Specify uniqueResolution to store the flag saying whether the resolution was unambiguous; // when NULL, throw an AmbiguousResolutionException upon hitting ambiguous SVM resolution. + // The 'level' parameter specifies the load level for the class containing the resolved MethodDesc. MethodDesc *ResolveVirtualStaticMethod( MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented = FALSE, BOOL allowVariantMatches = TRUE, - BOOL *uniqueResolution = NULL); + BOOL *uniqueResolution = NULL, + ClassLoadLevel level = CLASS_LOADED); // Try a partial resolve of the constraint call, up to generic code sharing. // @@ -2179,7 +2181,8 @@ class MethodTable MethodTable *pObjectMT, MethodDesc **ppDefaultMethod, BOOL allowVariance, - BOOL throwOnConflict); + BOOL throwOnConflict, + ClassLoadLevel level = CLASS_LOADED); #endif // DACCESS_COMPILE DispatchSlot FindDispatchSlot(UINT32 typeID, UINT32 slotNumber, BOOL throwOnConflict); @@ -2218,7 +2221,7 @@ class MethodTable // Try to resolve a given static virtual method override on this type. Return nullptr // when not found. - MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented); + MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented, ClassLoadLevel level); public: static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl); diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.cs.template b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.cs.template new file mode 100644 index 00000000000000..318418b5304d40 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.cs.template @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +interface IBase +{ + static abstract void Method(); +} + +interface IDerived : IBase +{ + static void IBase.Method() + { + Console.WriteLine("IDerived.Method"); + } +} + +class Final : IDerived +{ +} + +class Program +{ + private static void CallSVM() + where T : IBase + { + T.Method(); + } + + static int Main() + { + CallSVM(); + return 100; + } +} diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.il b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.il new file mode 100644 index 00000000000000..3116af1f532edc --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.il @@ -0,0 +1,135 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18408 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern System.Runtime +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 7:0:0:0 +} +.assembly extern System.Console +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 7:0:0:0 +} +.assembly RecursiveGeneric +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // + // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module RecursiveGeneric.dll +// MVID: {10541B0F-16D6-4F9A-B0EB-E793F524F163} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x03000000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class interface private abstract auto ansi IBase`1 +{ + .method public hidebysig static abstract virtual + void Method() cil managed + { + } // end of method IBase`1::Method + +} // end of class IBase`1 + +.class interface private abstract auto ansi IDerived`1 + implements class IBase`1 +{ + .method public hidebysig static void Method() cil managed + { + .override method void class IBase`1::Method() + // + .maxstack 8 + IL_0000: nop + IL_0001: ldstr "Final.Method" + IL_0006: call void [System.Console]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ret + } // end of method Final::Method + +} // end of class IDerived`1 + +.class private auto ansi beforefieldinit Final + extends [System.Runtime]System.Object + implements class IDerived`1, + class IBase`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [System.Runtime]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Final::.ctor + +} // end of class Final + +.class private auto ansi beforefieldinit Program + extends [System.Runtime]System.Object +{ + .method private hidebysig static void CallSVM<(class IBase`1) T,U>() cil managed + { + // + .maxstack 8 + IL_0000: nop + IL_0001: constrained. !!T + IL_0007: call void class IBase`1::Method() + IL_000c: nop + IL_000d: ret + } // end of method Program::CallSVM + + .method private hidebysig static int32 + Main() cil managed + { + .entrypoint + // + .maxstack 1 + .locals init ([0] int32 V_0) + IL_0000: nop + IL_0001: call void Program::CallSVM() + IL_0006: nop + IL_0007: ldc.i4.s 100 + IL_0009: stloc.0 + IL_000a: br.s IL_000c + + IL_000c: ldloc.0 + IL_000d: ret + } // end of method Program::Main + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [System.Runtime]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method Program::.ctor + +} // end of class Program + + +// ============================================================= + +// +// diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.ilproj b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.ilproj new file mode 100644 index 00000000000000..e1ac3464487ccd --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_70385.ilproj @@ -0,0 +1,11 @@ + + + Exe + + + Full + + + + +