diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md
index 6e1900c1dc6bd0..941db3382b20e1 100644
--- a/docs/design/specs/Ecma-335-Augments.md
+++ b/docs/design/specs/Ecma-335-Augments.md
@@ -453,7 +453,7 @@ https://www.ecma-international.org/publications-and-standards/standards/ecma-335
(Add second paragraph)
-Static interface methods may be marked as virtual. Valid object types implementing such interfaces shall provide implementations
+Static interface methods may be marked as virtual. Valid object types implementing such interfaces may provide implementations
for these methods by means of Method Implementations (II.15.1.4). Polymorphic behavior of calls to these methods is facilitated
by the constrained. call IL instruction where the constrained. prefix specifies the type to use for lookup of the static interface
method.
@@ -531,7 +531,6 @@ or static method actually implemented directly on the type.
(Add to the end of the 1st paragraph)
Interfaces may define static virtual methods that get resolved at runtime based on actual types involved.
-These static virtual methods must be marked as abstract in the defining interfaces.
### II.12.2 Implementing virtual methods on interfaces
@@ -754,7 +753,7 @@ the call itself doesn't involve any instance or `this` pointer.
(Edit bulleted section "This contains informative text only" starting at the bottom of page
233):
-Edit section *7.b*: Static | Virtual | !Abstract
+Remove section *7.b*: ~~Static | Virtual~~
(Add new section 41 after the last section 40:)
diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp
index c74e16b00d9970..56a40b0495972a 100644
--- a/src/coreclr/vm/genericdict.cpp
+++ b/src/coreclr/vm/genericdict.cpp
@@ -1081,10 +1081,11 @@ Dictionary::PopulateEntry(
#if FEATURE_DEFAULT_INTERFACES
// If we resolved the constrained call on a value type into a method on a reference type, this is a
// default interface method implementation.
+ // If the method is a static method, this is ok, but for instance methods there are boxing issues.
// In such case we would need to box the value type before we can dispatch to the implementation.
// This would require us to make a "boxing stub". For now we leave the boxing stubs unimplemented.
// It's not clear if anyone would need them and the implementation complexity is not worth it at this time.
- if (!pResolvedMD->GetMethodTable()->IsValueType() && constraintType.GetMethodTable()->IsValueType())
+ if (!pResolvedMD->IsStatic() && !pResolvedMD->GetMethodTable()->IsValueType() && constraintType.GetMethodTable()->IsValueType())
{
SString assemblyName;
diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index a81000cb0d0b24..873db55f9422d4 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -7580,7 +7580,6 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc*
{
canonicalEquivalentFound = true;
break;
- return NULL;
}
}
}
@@ -7644,6 +7643,12 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc*
}
}
}
+
+ // Default implementation logic, which only kicks in for default implementations when lookin up on an exact interface target
+ if (!pInterfaceMD->IsAbstract() && !(this == g_pCanonMethodTableClass) && !IsSharedByGenericInstantiations())
+ {
+ return pInterfaceMD->FindOrCreateAssociatedMethodDesc(pInterfaceMD, pInterfaceType, FALSE, pInterfaceMD->GetMethodInstantiation(), FALSE);
+ }
}
if (allowNullResult)
@@ -7818,7 +7823,7 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented()
MethodDesc *pMD = it.GetMethodDesc();
if (pMD->IsVirtual() &&
pMD->IsStatic() &&
- !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE))
+ (pMD->IsAbstract() && !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE)))
{
IMDInternalImport* pInternalImport = GetModule()->GetMDImport();
GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL);
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index 19a63dd9255443..479bf176691485 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -5076,7 +5076,7 @@ MethodTableBuilder::ValidateMethods()
// Virtual static methods are only allowed on interfaces and they must be abstract.
if (IsMdStatic(it.Attrs()) && IsMdVirtual(it.Attrs()))
{
- if (!IsInterface() || !IsMdAbstract(it.Attrs()))
+ if (!IsInterface())
{
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL, it.Token());
}
diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp
index 67818c5053fe1c..3b1a1c90ce0825 100644
--- a/src/coreclr/vm/typedesc.cpp
+++ b/src/coreclr/vm/typedesc.cpp
@@ -1613,7 +1613,7 @@ BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstra
MethodDesc *pMD = it.GetMethodDesc();
if (pMD->IsVirtual() &&
pMD->IsStatic() &&
- !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE))
+ (pMD->IsAbstract() && !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE)))
{
virtualStaticResolutionCheckFailed = true;
break;
diff --git a/src/tests/Common/Directory.Build.targets b/src/tests/Common/Directory.Build.targets
index 1e0b3a791d4eed..4f31cd9fc7646c 100644
--- a/src/tests/Common/Directory.Build.targets
+++ b/src/tests/Common/Directory.Build.targets
@@ -172,7 +172,8 @@
-
+
+
diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/GenericContext/Generator/Program.cs b/src/tests/Loader/classloader/StaticVirtualMethods/GenericContext/Generator/Program.cs
index 3ece4ed1e8dada..88d718a3432b4e 100644
--- a/src/tests/Loader/classloader/StaticVirtualMethods/GenericContext/Generator/Program.cs
+++ b/src/tests/Loader/classloader/StaticVirtualMethods/GenericContext/Generator/Program.cs
@@ -100,6 +100,7 @@ static void EmitTestGlobalHeader(TextWriter tw)
tw.WriteLine("");
tw.WriteLine("// THIS FILE IS AUTOGENERATED EDIT Generator/Program.cs instead and rerun the generator");
tw.WriteLine(".assembly extern System.Console {}");
+ tw.WriteLine(".assembly extern xunit.core {}");
tw.WriteLine(".assembly extern mscorlib {}");
tw.WriteLine(".assembly extern System.Runtime {}");
tw.WriteLine(".assembly extern GenericContextCommonCs {}");
@@ -132,6 +133,83 @@ static void EmitCodeForCommonLibrary(TextWriter tw)
tw.WriteLine(@"{");
tw.WriteLine(@" .method public newslot virtual abstract static void NormalMethod() {}");
tw.WriteLine(@" .method public newslot virtual abstract static void GenericMethod() {}");
+ tw.WriteLine(@"}");
+ tw.WriteLine(@"");
+ tw.WriteLine(@".class interface public abstract auto ansi IFaceNonGenericDefaultImp");
+ tw.WriteLine(@"{");
+ tw.WriteLine(@" .method public newslot virtual static void NormalMethod()");
+ tw.WriteLine(@" {");
+ tw.WriteLine(@" ldstr ""IFaceNonGenericDefaultImp.NormalMethod""");
+ tw.WriteLine(@" stsfld string [GenericContextCommonCs]Statics::String");
+ tw.WriteLine(@" ret");
+ tw.WriteLine(@" }");
+ tw.WriteLine(@" .method public newslot virtual static void GenericMethod()");
+ tw.WriteLine(@" {");
+ tw.WriteLine(@" ldstr ""IFaceNonGenericDefaultImp.GenericMethod<""");
+ tw.WriteLine(@" ldtoken !!0");
+ tw.WriteLine(@" call string[GenericContextCommonCs] Statics::MakeName(valuetype[System.Runtime]System.RuntimeTypeHandle)");
+ tw.WriteLine(@" ldstr "">""");
+ tw.WriteLine(@" call string[System.Runtime] System.String::Concat(string, string, string)");
+ tw.WriteLine(@" stsfld string [GenericContextCommonCs]Statics::String");
+ tw.WriteLine(@" ret");
+ tw.WriteLine(@" }");
+ tw.WriteLine(@"}");
+ tw.WriteLine(@"");
+ tw.WriteLine(@".class interface public abstract auto ansi IFaceGenericDefaultImp`1");
+ tw.WriteLine(@"{");
+ tw.WriteLine(@" .method public newslot virtual static void NormalMethod()");
+ tw.WriteLine(@" {");
+ tw.WriteLine(@" ldstr ""IFaceGenericDefaultImp`1<""");
+ tw.WriteLine(@" ldtoken !0");
+ tw.WriteLine(@" call string[GenericContextCommonCs] Statics::MakeName(valuetype[System.Runtime]System.RuntimeTypeHandle)");
+ tw.WriteLine(@" ldstr "">.NormalMethod""");
+ tw.WriteLine(@" call string[System.Runtime] System.String::Concat(string, string, string)");
+ tw.WriteLine(@" stsfld string [GenericContextCommonCs]Statics::String");
+ tw.WriteLine(@" ret");
+ tw.WriteLine(@" }");
+ tw.WriteLine(@" .method public newslot virtual static void GenericMethod()");
+ tw.WriteLine(@" {");
+ tw.WriteLine(@" ldstr ""IFaceGenericDefaultImp`1<""");
+ tw.WriteLine(@" ldtoken !0");
+ tw.WriteLine(@" call string[GenericContextCommonCs] Statics::MakeName(valuetype[System.Runtime]System.RuntimeTypeHandle)");
+ tw.WriteLine(@" ldstr "">.GenericMethod<""");
+ tw.WriteLine(@" ldtoken !!0");
+ tw.WriteLine(@" call string[GenericContextCommonCs] Statics::MakeName(valuetype[System.Runtime]System.RuntimeTypeHandle)");
+ tw.WriteLine(@" ldstr "">""");
+ tw.WriteLine(@" call string[System.Runtime] System.String::Concat(string, string, string)");
+ tw.WriteLine(@" call string[System.Runtime] System.String::Concat(string, string, string)");
+ tw.WriteLine(@" stsfld string [GenericContextCommonCs]Statics::String");
+ tw.WriteLine(@" ret");
+ tw.WriteLine(@" }");
+ tw.WriteLine(@"}");
+ tw.WriteLine(@"");
+ tw.WriteLine(@".class interface public abstract auto ansi IFaceCuriouslyRecurringGenericDefaultImp`1<(class IFaceCuriouslyRecurringGenericDefaultImp`1) T> ");
+ tw.WriteLine(@"{");
+ tw.WriteLine(@" .method public newslot virtual static void NormalMethod()");
+ tw.WriteLine(@" {");
+ tw.WriteLine(@" ldstr ""IFaceCuriouslyRecurringGenericDefaultImp`1<""");
+ tw.WriteLine(@" ldtoken !0");
+ tw.WriteLine(@" call string[GenericContextCommonCs] Statics::MakeName(valuetype[System.Runtime]System.RuntimeTypeHandle)");
+ tw.WriteLine(@" ldstr "">.NormalMethod""");
+ tw.WriteLine(@" call string[System.Runtime] System.String::Concat(string, string, string)");
+ tw.WriteLine(@" stsfld string [GenericContextCommonCs]Statics::String");
+ tw.WriteLine(@" ret");
+ tw.WriteLine(@" }");
+ tw.WriteLine(@" .method public newslot virtual static void GenericMethod()");
+ tw.WriteLine(@" {");
+ tw.WriteLine(@" ldstr ""IFaceCuriouslyRecurringGenericDefaultImp`1<""");
+ tw.WriteLine(@" ldtoken !0");
+ tw.WriteLine(@" call string[GenericContextCommonCs] Statics::MakeName(valuetype[System.Runtime]System.RuntimeTypeHandle)");
+ tw.WriteLine(@" ldstr "">.GenericMethod<""");
+ tw.WriteLine(@" ldtoken !!0");
+ tw.WriteLine(@" call string[GenericContextCommonCs] Statics::MakeName(valuetype[System.Runtime]System.RuntimeTypeHandle)");
+ tw.WriteLine(@" ldstr "">""");
+ tw.WriteLine(@" call string[System.Runtime] System.String::Concat(string, string, string)");
+ tw.WriteLine(@" call string[System.Runtime] System.String::Concat(string, string, string)");
+ tw.WriteLine(@" stsfld string [GenericContextCommonCs]Statics::String");
+ tw.WriteLine(@" ret");
+ tw.WriteLine(@" }");
+
tw.WriteLine(@"}");
tw.WriteLine(@"");
tw.WriteLine(@".class public sealed auto ansi GenericStruct`1");
@@ -232,7 +310,7 @@ static void EmitEndMethod(TextWriter tw, MethodDesc md)
tw.WriteLine($" }} // end of method {md.Name}");
}
- static void GenerateImplementations(TextWriter tw)
+ static void GenerateImplementations(TextWriter tw, bool testDefaultImplementations, string defaultImpSuffix)
{
foreach (var constrainedTypeDefinition in (ConstrainedTypeDefinition[])typeof(ConstrainedTypeDefinition).GetEnumValues())
{
@@ -259,17 +337,17 @@ static void GenerateImplementations(TextWriter tw)
if (constrainedTypeDefinition.ToString().StartsWith("Generic"))
{
- GenerateImpl(implClass.Name + "", "IFaceNonGeneric", "", false);
- GenerateImpl(implClass.Name + "", "IFaceGeneric`1", "", true);
- GenerateImpl(implClass.Name + "", "IFaceGeneric`1", "