Skip to content

Commit 9e45de4

Browse files
Add default method support to virtual statics (#64717)
- The preview feature version of virtual statics implemented for .NET 6 does not allow for the interface methods to have a default implementation. - With this change, we add support for the interface method having an actual implementation to CoreCLR. From what I can tell the Mono runtime already had such support - There are some small ECMA spec updates to allow this change that are also included In addition, I've taken the liberty to enable running the coreclr test suite on Mono on Windows. It needed a small amount of fixup.
1 parent e48b179 commit 9e45de4

21 files changed

+80260
-294
lines changed

docs/design/specs/Ecma-335-Augments.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ https://www.ecma-international.org/publications-and-standards/standards/ecma-335
453453

454454
(Add second paragraph)
455455

456-
Static interface methods may be marked as virtual. Valid object types implementing such interfaces shall provide implementations
456+
Static interface methods may be marked as virtual. Valid object types implementing such interfaces may provide implementations
457457
for these methods by means of Method Implementations (II.15.1.4). Polymorphic behavior of calls to these methods is facilitated
458458
by the constrained. call IL instruction where the constrained. prefix specifies the type to use for lookup of the static interface
459459
method.
@@ -531,7 +531,6 @@ or static method actually implemented directly on the type.
531531
(Add to the end of the 1st paragraph)
532532

533533
Interfaces may define static virtual methods that get resolved at runtime based on actual types involved.
534-
These static virtual methods must be marked as abstract in the defining interfaces.
535534

536535
### II.12.2 Implementing virtual methods on interfaces
537536

@@ -754,7 +753,7 @@ the call itself doesn't involve any instance or `this` pointer.
754753
(Edit bulleted section "This contains informative text only" starting at the bottom of page
755754
233):
756755

757-
Edit section *7.b*: Static | Virtual | !Abstract
756+
Remove section *7.b*: ~~Static | Virtual~~
758757

759758
(Add new section 41 after the last section 40:)
760759

src/coreclr/vm/genericdict.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1081,10 +1081,11 @@ Dictionary::PopulateEntry(
10811081
#if FEATURE_DEFAULT_INTERFACES
10821082
// If we resolved the constrained call on a value type into a method on a reference type, this is a
10831083
// default interface method implementation.
1084+
// If the method is a static method, this is ok, but for instance methods there are boxing issues.
10841085
// In such case we would need to box the value type before we can dispatch to the implementation.
10851086
// This would require us to make a "boxing stub". For now we leave the boxing stubs unimplemented.
10861087
// It's not clear if anyone would need them and the implementation complexity is not worth it at this time.
1087-
if (!pResolvedMD->GetMethodTable()->IsValueType() && constraintType.GetMethodTable()->IsValueType())
1088+
if (!pResolvedMD->IsStatic() && !pResolvedMD->GetMethodTable()->IsValueType() && constraintType.GetMethodTable()->IsValueType())
10881089
{
10891090
SString assemblyName;
10901091

src/coreclr/vm/methodtable.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -7580,7 +7580,6 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc*
75807580
{
75817581
canonicalEquivalentFound = true;
75827582
break;
7583-
return NULL;
75847583
}
75857584
}
75867585
}
@@ -7644,6 +7643,12 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc*
76447643
}
76457644
}
76467645
}
7646+
7647+
// Default implementation logic, which only kicks in for default implementations when lookin up on an exact interface target
7648+
if (!pInterfaceMD->IsAbstract() && !(this == g_pCanonMethodTableClass) && !IsSharedByGenericInstantiations())
7649+
{
7650+
return pInterfaceMD->FindOrCreateAssociatedMethodDesc(pInterfaceMD, pInterfaceType, FALSE, pInterfaceMD->GetMethodInstantiation(), FALSE);
7651+
}
76477652
}
76487653

76497654
if (allowNullResult)
@@ -7818,7 +7823,7 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented()
78187823
MethodDesc *pMD = it.GetMethodDesc();
78197824
if (pMD->IsVirtual() &&
78207825
pMD->IsStatic() &&
7821-
!ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE))
7826+
(pMD->IsAbstract() && !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE)))
78227827
{
78237828
IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
78247829
GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL);

src/coreclr/vm/methodtablebuilder.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -5076,7 +5076,7 @@ MethodTableBuilder::ValidateMethods()
50765076
// Virtual static methods are only allowed on interfaces and they must be abstract.
50775077
if (IsMdStatic(it.Attrs()) && IsMdVirtual(it.Attrs()))
50785078
{
5079-
if (!IsInterface() || !IsMdAbstract(it.Attrs()))
5079+
if (!IsInterface())
50805080
{
50815081
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL, it.Token());
50825082
}

src/coreclr/vm/typedesc.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1613,7 +1613,7 @@ BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstra
16131613
MethodDesc *pMD = it.GetMethodDesc();
16141614
if (pMD->IsVirtual() &&
16151615
pMD->IsStatic() &&
1616-
!thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE))
1616+
(pMD->IsAbstract() && !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE)))
16171617
{
16181618
virtualStaticResolutionCheckFailed = true;
16191619
break;

src/tests/Common/Directory.Build.targets

+2-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@
172172
</ItemGroup>
173173

174174
<ItemGroup Condition="'$(RuntimeFlavor)' == 'mono' and '$(IsDesktopOS)' == 'true' " >
175-
<RuntimeDependencyCopyLocal Include="$(MonoArtifactsPath)/libcoreclr$(LibSuffix)" TargetDir="" />
175+
<RuntimeDependencyCopyLocal Condition="'$(TargetOS)' != 'windows'" Include="$(MonoArtifactsPath)/libcoreclr$(LibSuffix)" TargetDir="" />
176+
<RuntimeDependencyCopyLocal Condition="'$(TargetOS)' == 'windows'" Include="$(MonoArtifactsPath)coreclr$(LibSuffix)" TargetDir="" />
176177
<RuntimeDependencyCopyLocal Include="$(MonoArtifactsPath)/libmono-component-*" TargetDir="" />
177178
<RuntimeDependencyCopyLocal Include="$(MonoArtifactsPath)/*.dll" TargetDir="/" />
178179
</ItemGroup>

0 commit comments

Comments
 (0)