Skip to content

Commit 20c68d2

Browse files
authored
Fix for issue #80350 tracking runtime deficiencies in the presence of static virtual method reabstraction (#80987)
This change fixes the issue #80350 that uncovered several inconsistencies in the CoreCLR runtime w.r.t. static virtual methods. In particular, reabstraction (declaring an abstract explicit override of a static virtual method in a derived interface) turned out to be causing runtime issues. As part of the change I'm also fixing the reabstraction unit test svm_diamondshape as it turns out Roslyn emits the method flags in a slightly different manner than I originally expected in the hand-written IL test. Based on discussion with the Mono team I have had to temporarily disable the svm_diamondshape test on Mono before the correct fix is found. Thanks Tomas
1 parent f8e6ebf commit 20c68d2

5 files changed

Lines changed: 74 additions & 7 deletions

File tree

src/coreclr/vm/methodtablebuilder.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,7 +2868,8 @@ MethodTableBuilder::EnumerateClassMethods()
28682868
}
28692869

28702870
// Check for the presence of virtual static methods
2871-
if (IsMdVirtual(dwMemberAttrs) && IsMdStatic(dwMemberAttrs))
2871+
bool isStaticVirtual = (IsMdVirtual(dwMemberAttrs) && IsMdStatic(dwMemberAttrs));
2872+
if (isStaticVirtual)
28722873
{
28732874
bmtProp->fHasVirtualStaticMethods = TRUE;
28742875
}
@@ -3202,7 +3203,7 @@ MethodTableBuilder::EnumerateClassMethods()
32023203
BuildMethodTableThrowException(BFA_GENERIC_METHODS_INST);
32033204
}
32043205

3205-
// count how many overrides this method does All methods bodies are defined
3206+
// Check if the method is a MethodImpl body. All method bodies are defined
32063207
// on this type so we can just compare the tok with the body token found
32073208
// from the overrides.
32083209
implType = METHOD_IMPL_NOT;
@@ -3215,6 +3216,13 @@ MethodTableBuilder::EnumerateClassMethods()
32153216
}
32163217
}
32173218

3219+
if (implType == METHOD_IMPL && isStaticVirtual && IsMdAbstract(dwMemberAttrs))
3220+
{
3221+
// Don't record reabstracted static virtual methods as they don't constitute
3222+
// actual method slots, they're just markers used by the static virtual lookup.
3223+
continue;
3224+
}
3225+
32183226
// For delegates we don't allow any non-runtime implemented bodies
32193227
// for any of the four special methods
32203228
if (IsDelegate() && !IsMiRuntime(dwImplFlags))
@@ -4788,7 +4796,11 @@ VOID MethodTableBuilder::TestMethodImpl(
47884796
{
47894797
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NONVIRTUAL_DECL);
47904798
}
4791-
if ((IsMdVirtual(dwImplAttrs) && IsMdStatic(dwImplAttrs)) || (!IsMdVirtual(dwImplAttrs) && !IsMdStatic(dwImplAttrs)))
4799+
if (!!IsMdVirtual(dwImplAttrs) != !!IsMdAbstract(dwImplAttrs) && IsMdStatic(dwImplAttrs))
4800+
{
4801+
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL);
4802+
}
4803+
if (!IsMdVirtual(dwImplAttrs) && !IsMdStatic(dwImplAttrs))
47924804
{
47934805
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL);
47944806
}
@@ -5636,7 +5648,10 @@ MethodTableBuilder::ProcessMethodImpls()
56365648
DeclaredMethodIterator it(*this);
56375649
while (it.Next())
56385650
{
5639-
if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && bmtProp->fNoSanityChecks)
5651+
bool isVirtualStaticOverride = it.IsMethodImpl() && IsMdStatic(it.Attrs()) &&
5652+
!!IsMdVirtual(it.Attrs()) == !!IsMdAbstract(it.Attrs());
5653+
5654+
if (isVirtualStaticOverride && bmtProp->fNoSanityChecks)
56405655
{
56415656
// Non-virtual methods can only be classified as methodImpl when implementing
56425657
// static virtual methods.
@@ -5819,7 +5834,7 @@ MethodTableBuilder::ProcessMethodImpls()
58195834
}
58205835

58215836
// 3. Find the matching method.
5822-
declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, !IsMdVirtual(it.Attrs())); // Search for statics when the impl is non-virtual
5837+
declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, isVirtualStaticOverride); // Search for statics when the impl is non-virtual
58235838
}
58245839
else
58255840
{
@@ -5854,7 +5869,7 @@ MethodTableBuilder::ProcessMethodImpls()
58545869
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token());
58555870
}
58565871

5857-
if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && IsMdStatic(it.Attrs()))
5872+
if (isVirtualStaticOverride)
58585873
{
58595874
// Non-virtual methods can only be classified as methodImpl when implementing
58605875
// static virtual methods.

src/tests/Loader/classloader/StaticVirtualMethods/DiamondShape/svm_diamondshape.il

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@
232232
.class interface private abstract auto ansi I4Reabstract
233233
implements I4
234234
{
235-
.method private hidebysig abstract
235+
.method private hidebysig virtual abstract
236236
static int32 Func(int32 a) cil managed
237237
{
238238
.override method int32 I1::Func(int32)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
6+
// This regression test tracks the issue where implementation of a static virtual method
7+
// on a derived type is not found when there is a re-abstraction of the same method
8+
// higher in inheritance hierarchy.
9+
10+
class Test1 : I2
11+
{
12+
13+
static int Main()
14+
{
15+
string result = Test<Test1>();
16+
const string expectedResult = "Test1.M1";
17+
Console.WriteLine("Expected {0}, found {1}: {2}", expectedResult, result, expectedResult == result ? "match" : "mismatch");
18+
return result == expectedResult ? 100 : 101;
19+
}
20+
21+
static string Test<i1>() where i1 : I1
22+
{
23+
return i1.M1();
24+
}
25+
26+
static string I1.M1()
27+
{
28+
return "Test1.M1";
29+
}
30+
}
31+
32+
public interface I1
33+
{
34+
static abstract string M1();
35+
}
36+
37+
public interface I2 : I1
38+
{
39+
static abstract string I1.M1();
40+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
4+
<OutputType>Exe</OutputType>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="$(MSBuildProjectName).cs" />
8+
</ItemGroup>
9+
</Project>

src/tests/issues.targets

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,9 @@
15251525
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/CompatibleWithTest/**">
15261526
<Issue>Doesn't pass after LLVM AOT compilation.</Issue>
15271527
</ExcludeList>
1528+
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/StaticVirtualMethods/DiamondShape/**">
1529+
<Issue>https://github.com/dotnet/runtime/issues/80350</Issue>
1530+
</ExcludeList>
15281531
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/StaticVirtualMethods/InterfaceVariance/**">
15291532
<Issue>Static virtual methods are not yet implemented in the Mono runtime.</Issue>
15301533
</ExcludeList>

0 commit comments

Comments
 (0)