diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 431b49aa0c98c1..35a7ddb0360a6d 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -2868,7 +2868,8 @@ MethodTableBuilder::EnumerateClassMethods() } // Check for the presence of virtual static methods - if (IsMdVirtual(dwMemberAttrs) && IsMdStatic(dwMemberAttrs)) + bool isStaticVirtual = (IsMdVirtual(dwMemberAttrs) && IsMdStatic(dwMemberAttrs)); + if (isStaticVirtual) { bmtProp->fHasVirtualStaticMethods = TRUE; } @@ -3202,7 +3203,7 @@ MethodTableBuilder::EnumerateClassMethods() BuildMethodTableThrowException(BFA_GENERIC_METHODS_INST); } - // count how many overrides this method does All methods bodies are defined + // Check if the method is a MethodImpl body. All method bodies are defined // on this type so we can just compare the tok with the body token found // from the overrides. implType = METHOD_IMPL_NOT; @@ -3215,6 +3216,13 @@ MethodTableBuilder::EnumerateClassMethods() } } + if (implType == METHOD_IMPL && isStaticVirtual && IsMdAbstract(dwMemberAttrs)) + { + // Don't record reabstracted static virtual methods as they don't constitute + // actual method slots, they're just markers used by the static virtual lookup. + continue; + } + // For delegates we don't allow any non-runtime implemented bodies // for any of the four special methods if (IsDelegate() && !IsMiRuntime(dwImplFlags)) @@ -4788,7 +4796,11 @@ VOID MethodTableBuilder::TestMethodImpl( { BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NONVIRTUAL_DECL); } - if ((IsMdVirtual(dwImplAttrs) && IsMdStatic(dwImplAttrs)) || (!IsMdVirtual(dwImplAttrs) && !IsMdStatic(dwImplAttrs))) + if (!!IsMdVirtual(dwImplAttrs) != !!IsMdAbstract(dwImplAttrs) && IsMdStatic(dwImplAttrs)) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL); + } + if (!IsMdVirtual(dwImplAttrs) && !IsMdStatic(dwImplAttrs)) { BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL); } @@ -5636,7 +5648,10 @@ MethodTableBuilder::ProcessMethodImpls() DeclaredMethodIterator it(*this); while (it.Next()) { - if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && bmtProp->fNoSanityChecks) + bool isVirtualStaticOverride = it.IsMethodImpl() && IsMdStatic(it.Attrs()) && + !!IsMdVirtual(it.Attrs()) == !!IsMdAbstract(it.Attrs()); + + if (isVirtualStaticOverride && bmtProp->fNoSanityChecks) { // Non-virtual methods can only be classified as methodImpl when implementing // static virtual methods. @@ -5819,7 +5834,7 @@ MethodTableBuilder::ProcessMethodImpls() } // 3. Find the matching method. - declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, !IsMdVirtual(it.Attrs())); // Search for statics when the impl is non-virtual + declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, isVirtualStaticOverride); // Search for statics when the impl is non-virtual } else { @@ -5854,7 +5869,7 @@ MethodTableBuilder::ProcessMethodImpls() BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token()); } - if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && IsMdStatic(it.Attrs())) + if (isVirtualStaticOverride) { // Non-virtual methods can only be classified as methodImpl when implementing // static virtual methods. diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il index 50c83313560ff8..92e3934e683e27 100644 --- a/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il +++ b/src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il @@ -232,7 +232,7 @@ .class interface private abstract auto ansi I4Reabstract implements I4 { - .method private hidebysig abstract + .method private hidebysig virtual abstract static int32 Func(int32 a) cil managed { .override method int32 I1::Func(int32) diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_80350.cs b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_80350.cs new file mode 100644 index 00000000000000..8d15e9ea1509da --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_80350.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +// This regression test tracks the issue where implementation of a static virtual method +// on a derived type is not found when there is a re-abstraction of the same method +// higher in inheritance hierarchy. + +class Test1 : I2 +{ + + static int Main() + { + string result = Test(); + const string expectedResult = "Test1.M1"; + Console.WriteLine("Expected {0}, found {1}: {2}", expectedResult, result, expectedResult == result ? "match" : "mismatch"); + return result == expectedResult ? 100 : 101; + } + + static string Test() where i1 : I1 + { + return i1.M1(); + } + + static string I1.M1() + { + return "Test1.M1"; + } +} + +public interface I1 +{ + static abstract string M1(); +} + +public interface I2 : I1 +{ + static abstract string I1.M1(); +} diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_80350.csproj b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_80350.csproj new file mode 100644 index 00000000000000..7f0c12d57d8e5b --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/RegressionTests/GitHub_80350.csproj @@ -0,0 +1,9 @@ + + + true + Exe + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 546a55ae3016d1..71cb50f4000358 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1525,6 +1525,9 @@ Doesn't pass after LLVM AOT compilation. + + https://github.com/dotnet/runtime/issues/80350 + Static virtual methods are not yet implemented in the Mono runtime.