Allow reabstraction of default interface methods (#23313)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Mon, 8 Apr 2019 11:14:39 +0000 (13:14 +0200)
committerGitHub <noreply@github.com>
Mon, 8 Apr 2019 11:14:39 +0000 (13:14 +0200)
Allow the runtime to load types with incomplete interface implementations. With this change, we allow (in pseudo-C#):

```csharp
interface IFoo { void Frob() { } }
interface IBar : IFoo { abstract void IFoo.Frob() }
class Fooer : IBar { }
```

Calling IFoo.Frob on an instance of `Fooer` will result in new exception being thrown because the default implementation of `IFoo.Frob` was re-abstracted by `IBar`.

13 files changed:
Documentation/design-docs/default-interface-methods.md
src/dlls/mscorrc/mscorrc.rc
src/dlls/mscorrc/resource.h
src/vm/methodtable.cpp
src/vm/methodtablebuilder.cpp
src/vm/runtimehandles.cpp
tests/src/Loader/classloader/DefaultInterfaceMethods/reabstraction/reabstraction.il [new file with mode: 0644]
tests/src/Loader/classloader/DefaultInterfaceMethods/reabstraction/reabstraction.ilproj [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/Emit.cs
tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapConsumer.cs
tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapProvider.il
tests/src/reflection/DefaultInterfaceMethods/InvokeConsumer.cs
tests/src/reflection/DefaultInterfaceMethods/InvokeProvider.il

index 738ae0c..0e9e761 100644 (file)
@@ -41,12 +41,13 @@ The algorithm is amended as follows:
     * If the interface method itself is not abstract, add it to the list.
     * Apply all MethodImpls specified in the list of interfaces implicitly implemented by the runtime class of the instance through which the interface method is invoked and add the methods to the list.
     * Go over the owning types of each of the candidate methods in the list. If the owning type is less concrete than some other type in the list (there is another method in the list whose owning type requires the less concrete type), remove it from the list.
-    * If there's more than one method in the list, throw NotSupportedException
-    * If there's exactly one method in the list call that method
+    * If there's more than one method in the list, throw AmbiguousImplementationException
+    * If there's exactly one method in the list and the method is not abstract, call that method
+    * If there's exactly one method in the list but the method is abstract, throw `EntryPointNotFoundException`.
     * If there's no method in the list and the interface is variant, repeat the above algorithm, looking for a variant match. Return the first variant match provided by a most specific interface.
 
 **Section** "III.2.1 constrained. prefix" the paragraph starting with "This last case can only occur when method was defined on `System.Object`, `System.ValueType`, or `System.Enum`" is extended to also cover default interface method implementation. In the case the interface method implementation is provided by an interface, the implicit boxing becomes _observable_ to the program.
 
-**Section** "III.4.2 callvirt" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method.
+**Section** "III.4.2 callvirt" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract.
 
-**Section** "III.4.18 ldvirtftn" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method.
+**Section** "III.4.18 ldvirtftn" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract.
index 7acfb09..c733f69 100644 (file)
@@ -733,6 +733,7 @@ BEGIN
     IDS_CLASSLOAD_MI_FINAL_IMPL             "Method implementation on an interface '%1' from assembly '%2' must be a final method."
     IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE        "Could not call method '%1' on interface '%2' with type '%3' from assembly '%4' because there are multiple incompatible interface methods overriding this method."
     IDS_CLASSLOAD_UNSUPPORTED_DISPATCH      "Could not make constrained call to method '%1' on interface '%2' with type '%3' from assembly '%4'. Dispatch to default interface methods is not supported in this situation."
+    IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED    "Could not call method '%1' on type '%2' with an instance of '%3' from assembly '%4' because there is no implementation for the method."
 
     IDS_CLASSLOAD_MISSINGMETHODRVA          "Could not load type '%1' from assembly '%2' because the method '%3' has no implementation (no RVA)."
     SECURITY_E_INCOMPATIBLE_EVIDENCE        "Assembly '%1' already loaded without additional security evidence."
index cc9e0e8..f11aa12 100644 (file)
 #define IDS_CLASSLOAD_MI_FINAL_IMPL             0x1ac8
 #define IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE        0x1ac9
 #define IDS_CLASSLOAD_UNSUPPORTED_DISPATCH      0x1aca
+#define IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED    0x1acb
 
 #define BFA_INVALID_TOKEN_TYPE                  0x2001
 #define BFA_INVALID_TOKEN                       0x2003
index 1603484..34381fa 100644 (file)
@@ -6944,6 +6944,39 @@ BOOL MethodTable::FindDispatchEntry(UINT32 typeID,
     RETURN (FALSE);
 }
 
+#ifndef DACCESS_COMPILE
+
+void ThrowExceptionForAbstractOverride(
+    MethodTable *pTargetClass,
+    MethodTable *pInterfaceMT,
+    MethodDesc *pInterfaceMD)
+{
+    LIMITED_METHOD_CONTRACT;
+
+    SString assemblyName;
+
+    pTargetClass->GetAssembly()->GetDisplayName(assemblyName);
+
+    SString strInterfaceName;
+    TypeString::AppendType(strInterfaceName, TypeHandle(pInterfaceMT));
+
+    SString strMethodName;
+    TypeString::AppendMethod(strMethodName, pInterfaceMD, pInterfaceMD->GetMethodInstantiation());
+
+    SString strTargetClassName;
+    TypeString::AppendType(strTargetClassName, pTargetClass);
+
+    COMPlusThrow(
+        kEntryPointNotFoundException,
+        IDS_CLASSLOAD_METHOD_NOT_IMPLEMENTED,
+        strMethodName,
+        strInterfaceName,
+        strTargetClassName,
+        assemblyName);
+}
+
+#endif // !DACCESS_COMPILE
+
 //==========================================================================================
 // Possible cases:
 //      1. Typed (interface) contract
@@ -7063,15 +7096,32 @@ MethodTable::FindDispatchImpl(
 
                 if (foundDefaultInterfaceImplementation)
                 {
-                    // Now, construct a DispatchSlot to return in *pImplSlot
-                    DispatchSlot ds(pDefaultMethod->GetMethodEntryPoint());
-
-                    if (pImplSlot != NULL)
+                    //
+                    // If the default implementation we found is abstract, we hit a reabstraction.
+                    //
+                    // interface IFoo { void Frob() { ... } }
+                    // interface IBar { abstract void IFoo.Frob() }
+                    // class Foo : IBar { /* IFoo.Frob not implemented here */ }
+                    //
+                    if (pDefaultMethod->IsAbstract())
                     {
-                        *pImplSlot = ds;
+                        if (throwOnConflict)
+                        {
+                            ThrowExceptionForAbstractOverride(this, pIfcMT, pIfcMD);
+                        }
                     }
+                    else
+                    {
+                        // Now, construct a DispatchSlot to return in *pImplSlot
+                        DispatchSlot ds(pDefaultMethod->GetMethodEntryPoint());
 
-                    RETURN(TRUE);
+                        if (pImplSlot != NULL)
+                        {
+                            *pImplSlot = ds;
+                        }
+
+                        RETURN(TRUE);
+                    }
                 }
             }
 
index b946026..286cd74 100644 (file)
@@ -10772,6 +10772,18 @@ BOOL MethodTableBuilder::HasDefaultInterfaceImplementation(bmtRTType *pDeclType,
     if (!pDeclMD->IsAbstract())
         return TRUE;
 
+    // If the method is an abstract MethodImpl, this is a reabstraction:
+    //
+    // interface IFoo { void Frob() { } }
+    // interface IBar : IFoo { abstract void IFoo.Frob() }
+    //
+    // We don't require these to have an implementation because they're final anyway.
+    if (pDeclMD->IsMethodImpl())
+    {
+        assert(pDeclMD->IsFinal());
+        return TRUE;
+    }
+
     int targetSlot = pDeclMD->GetSlot();
 
     // Iterate over all the interfaces this type implements
index 9acb47f..c25bcbf 100644 (file)
@@ -1165,7 +1165,7 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementation(Enregi
         // with at least an abstract method. b19897_GetInterfaceMap_Abstract.exe tests this case.
         //@TODO:STUBDISPATCH: Don't need to track down the implementation, just the declaration, and this can
         //@TODO:              be done faster - just need to make a function FindDispatchDecl.
-        DispatchSlot slot(typeHandle.GetMethodTable()->FindDispatchSlotForInterfaceMD(thOwnerOfMD, pMD, TRUE /* throwOnConflict */));
+        DispatchSlot slot(typeHandle.GetMethodTable()->FindDispatchSlotForInterfaceMD(thOwnerOfMD, pMD, FALSE /* throwOnConflict */));
     if (!slot.IsNull())
             pResult = slot.GetMethodDesc();
 
diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/reabstraction/reabstraction.il b/tests/src/Loader/classloader/DefaultInterfaceMethods/reabstraction/reabstraction.il
new file mode 100644 (file)
index 0000000..18984ce
--- /dev/null
@@ -0,0 +1,218 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+.assembly extern mscorlib { }
+
+.assembly reabstraction { }
+
+// Interface with a default method
+.class interface public abstract auto ansi I1
+{
+  .method public hidebysig newslot virtual
+          instance int32 Add(int32 x) cil managed
+  {
+    ldc.i4.1
+    ldarg.1
+    add
+    ret
+  }
+}
+
+// Interface that reabstracts the default method
+.class interface public abstract auto ansi I2
+  implements I1
+{
+  .method public hidebysig newslot virtual abstract final
+          instance int32 Add(int32 x) cil managed
+  {
+    .override I1::Add
+  }
+}
+
+// Interface that overrides reabstracted method
+.class interface public abstract auto ansi I3
+  implements I2
+{
+  .method public hidebysig newslot virtual final
+          instance int32 Add(int32 x) cil managed
+  {
+    .override I1::Add
+    ldc.i4.2
+    ldarg.1
+    add
+    ret
+  }
+}
+
+// Interface that overrides normal default method
+.class interface public abstract auto ansi I4
+  implements I1
+{
+  .method public hidebysig newslot virtual final
+          instance int32 Add(int32 x) cil managed
+  {
+    .override I1::Add
+    ldc.i4.3
+    ldarg.1
+    add
+    ret
+  }
+}
+
+// Plain old interface
+.class interface public abstract auto ansi I5
+{
+  .method public hidebysig newslot virtual abstract
+          instance int32 Add(int32 x) cil managed
+  {
+  }
+}
+
+// Interface that re-abstracts an already abstract method
+.class interface public abstract auto ansi I6
+  implements I5
+{
+  .method public hidebysig newslot virtual abstract final
+          instance int32 Add(int32 x) cil managed
+  {
+    .override I5::Add
+  }
+}
+
+// Class that doesn't implement reabstracted method
+.class public auto ansi C1
+  extends [mscorlib]System.Object
+  implements I2
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+// Class that implements reabstracted method
+.class public auto ansi C2
+  extends [mscorlib]System.Object
+  implements I3
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+// Class with a diamond between reabstracted and implemented default method
+.class public auto ansi C3
+  extends [mscorlib]System.Object
+  implements I2, I4
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+// Class that doesn't implement interface method that also never had an implementation
+.class public auto ansi C4
+  extends [mscorlib]System.Object
+  implements I6
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+.method private hidebysig static int32 Main() cil managed
+{
+  .entrypoint
+
+  .try
+  {
+    newobj instance void C1::.ctor()
+    ldc.i4.0
+    callvirt instance int32 I1::Add(int32)
+    pop
+    leave Fail
+  }
+  catch [mscorlib]System.EntryPointNotFoundException
+  {
+    pop
+    leave PureVirtualOK
+  }
+PureVirtualOK:
+
+  .try
+  {
+    newobj instance void C1::.ctor()
+    dup
+    ldvirtftn instance int32 I1::Add(int32)
+    newobj instance void class [mscorlib]System.Func`2<int32,int32>::.ctor(object,
+                                                                           native int)
+    pop
+    leave Fail
+  }
+  catch [mscorlib]System.EntryPointNotFoundException
+  {
+    pop
+    leave PureVirtualDelegateOK
+  }
+PureVirtualDelegateOK:
+
+  newobj instance void C2::.ctor()
+  ldc.i4.0
+  callvirt instance int32 I1::Add(int32)
+  ldc.i4.2
+  bne.un Fail
+
+  .try
+  {
+    newobj instance void C3::.ctor()
+    ldc.i4.0
+    callvirt instance int32 I1::Add(int32)
+    pop
+    leave Fail
+  }
+  catch [System.Private.CoreLib]System.Runtime.AmbiguousImplementationException
+  {
+    pop
+    leave DiamondCaseOK
+  }
+DiamondCaseOK:
+
+  .try
+  {
+    newobj instance void C4::.ctor()
+    ldc.i4.0
+    callvirt instance int32 I5::Add(int32)
+    pop
+    leave Fail
+  }
+  catch [mscorlib]System.EntryPointNotFoundException
+  {
+    pop
+    leave NeverImplementedOK
+  }
+NeverImplementedOK:
+
+  ldc.i4 100
+  ret
+
+Fail:
+  ldc.i4.m1
+  ret
+}
diff --git a/tests/src/Loader/classloader/DefaultInterfaceMethods/reabstraction/reabstraction.ilproj b/tests/src/Loader/classloader/DefaultInterfaceMethods/reabstraction/reabstraction.ilproj
new file mode 100644 (file)
index 0000000..b7b3b4e
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{85DFC527-4DB1-595E-A7D7-E94EE1F8140D}</ProjectGuid>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <ReferenceLocalMscorlib>true</ReferenceLocalMscorlib>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Include="reabstraction.il" />
+  </ItemGroup>
+
+
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
index e57f7c1..4cd872a 100644 (file)
@@ -73,14 +73,66 @@ class Program
         // Test what we created
         //
 
-        object o = Activator.CreateInstance(fooType);
-
         int result = 0;
 
-        result |= (int)ifooType.GetMethod("InstanceMethod").Invoke(o, null);
-        result |= (int)ifooType.GetMethod("DefaultMethod").Invoke(o, null);
-        result |= (int)ifooType.GetMethod("InterfaceMethod").Invoke(o, null);
+        {
+            object o = Activator.CreateInstance(fooType);
+
+            result |= (int)ifooType.GetMethod("InstanceMethod").Invoke(o, null);
+            result |= (int)ifooType.GetMethod("DefaultMethod").Invoke(o, null);
+            result |= (int)ifooType.GetMethod("InterfaceMethod").Invoke(o, null);
+        }
+
+        //
+        // Set up the IBaz interface
+        //
+
+        var ibazType = modb.DefineType("IBaz", TypeAttributes.Interface | TypeAttributes.Abstract | TypeAttributes.Public, null, new Type[] { ifooType });
+
+        // Override the default interface method on IFoo with a reabstraction
+        {
+            var mb = ibazType.DefineMethod("DefaultMethodImpl", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.Abstract, typeof(int), Type.EmptyTypes);
+            ibazType.DefineMethodOverride(mb, ifooType.GetMethod("DefaultMethod"));
+        }
+
+        // Override the regular interface method on IFoo with a reabstraction
+        {
+            var mb = ibazType.DefineMethod("InterfaceMethodImpl", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final | MethodAttributes.Abstract, typeof(int), Type.EmptyTypes);
+            ibazType.DefineMethodOverride(mb, ifooType.GetMethod("InterfaceMethod"));
+        }
+
+        ibazType.CreateTypeInfo();
+
+        //
+        // Make a simple Baz class that implements IBaz
+        //
+
+        var bazType = modb.DefineType("Baz", TypeAttributes.Class | TypeAttributes.Public, typeof(object), new Type[] { ibazType });
+
+        bazType.CreateTypeInfo();
+
+        {
+            object o = Activator.CreateInstance(bazType);
+
+            try
+            {
+                ifooType.GetMethod("DefaultMethod").Invoke(o, null);
+            }
+            catch (EntryPointNotFoundException)
+            {
+                result |= 0x10;
+            }
+
+            try
+            {
+                ifooType.GetMethod("InterfaceMethod").Invoke(o, null);
+            }
+            catch (EntryPointNotFoundException)
+            {
+                result |= 0x20;
+            }
+        }
 
-        return result == 0x07 ? 100 : result;
+        return result == 0x37 ? 100 : result;
     }
 }
index ecb6373..845cf49 100644 (file)
@@ -85,12 +85,86 @@ class Program
                 return 10;
         }
 
+
+        {
+            var map = typeof(Reabstractor).GetInterfaceMap(typeof(IFoo));
+
+            int foundMatchMask = 0;
+
+            MethodInfo ifooDefaultMethod = typeof(IFoo).GetMethod("DefaultMethod");
+            MethodInfo ifooOtherMethod = typeof(IFoo).GetMethod("OtherMethod");
+
+            for (int i = 0; i < map.InterfaceMethods.Length; i++)
+            {
+                MethodInfo declMethod = map.InterfaceMethods[i];
+                MethodInfo implMethod = map.TargetMethods[i];
+
+                Console.Write("{0} ({1}) - {2} ({3}) - ", declMethod, declMethod.DeclaringType, implMethod, implMethod?.DeclaringType);
+
+                if (declMethod.Equals(ifooDefaultMethod))
+                {
+                    foundMatchMask |= 1;
+                    CheckEqual(ref failed, implMethod, null);
+                }
+                else if (declMethod.Equals(ifooOtherMethod))
+                {
+                    foundMatchMask |= 2;
+                    CheckEqual(ref failed, implMethod, null);
+                }
+                else
+                {
+                    Console.WriteLine("UNEXPECTED");
+                    failed = true;
+                }
+            }
+
+            if (foundMatchMask != 3)
+                return 10;
+        }
+
+        {
+            var map = typeof(Diamond).GetInterfaceMap(typeof(IFoo));
+
+            int foundMatchMask = 0;
+
+            MethodInfo ifooDefaultMethod = typeof(IFoo).GetMethod("DefaultMethod");
+            MethodInfo ifooOtherMethod = typeof(IFoo).GetMethod("OtherMethod");
+
+            for (int i = 0; i < map.InterfaceMethods.Length; i++)
+            {
+                MethodInfo declMethod = map.InterfaceMethods[i];
+                MethodInfo implMethod = map.TargetMethods[i];
+
+                Console.Write("{0} ({1}) - {2} ({3}) - ", declMethod, declMethod.DeclaringType, implMethod, implMethod?.DeclaringType);
+
+                if (declMethod.Equals(ifooDefaultMethod))
+                {
+                    foundMatchMask |= 1;
+                    CheckEqual(ref failed, implMethod, ifooDefaultMethod);
+                }
+                else if (declMethod.Equals(ifooOtherMethod))
+                {
+                    foundMatchMask |= 2;
+                    CheckEqual(ref failed, implMethod, null);
+                }
+                else
+                {
+                    Console.WriteLine("UNEXPECTED");
+                    failed = true;
+                }
+            }
+
+            if (foundMatchMask != 3)
+                return 10;
+        }
+
+
         return failed ? -1 : 100;
     }
 
     static void CheckEqual(ref bool failed, MethodInfo method1, MethodInfo method2)
     {
-        if (method1.Equals(method2))
+        if (Object.Equals(method1, method2))
             Console.WriteLine("OK");
         else
         {
@@ -98,4 +172,4 @@ class Program
             failed = true;
         }
     }
-}
\ No newline at end of file
+}
index 0378598..9ad1a7d 100644 (file)
   }
 }
 
+.class interface public abstract auto ansi IBaz implements IFoo
+{
+  .method public hidebysig newslot virtual final instance int32 OtherMethod(int32) cil managed
+  {
+    .override IFoo::OtherMethod
+    ldarg.1
+    ret
+  }
+}
+
+.class interface public abstract auto ansi IReabstractor implements IFoo
+{
+  .method public hidebysig newslot abstract virtual final instance int32 DefaultMethod(int32) cil managed
+  {
+    .override IFoo::DefaultMethod
+  }
+
+  .method public hidebysig newslot abstract virtual final instance int32 OtherMethod(int32) cil managed
+  {
+    .override IFoo::OtherMethod
+  }
+}
+
 .class interface public abstract auto ansi IFoo`1<T>
 {
   .method public hidebysig newslot virtual instance valuetype [mscorlib]System.RuntimeTypeHandle DefaultMethod() cil managed
     ret
   }
 }
+
+.class public auto ansi beforefieldinit Reabstractor
+       extends [mscorlib]System.Object
+       implements IReabstractor
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+.class public auto ansi beforefieldinit Diamond
+       extends [mscorlib]System.Object
+       implements IBar, IBaz
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
index 4ac82f9..247b830 100644 (file)
@@ -38,6 +38,15 @@ class Program
         if (!((RuntimeTypeHandle)typeof(IFoo<Fooer>).GetMethod("InstanceMethod").Invoke(new ValueFooer(), new object[] { })).Equals(typeof(Fooer[]).TypeHandle))
             return 33;
 
+        try
+        {
+            typeof(IFoo).GetMethod("DefaultMethod").Invoke(new Reabstractor(), new object[] { 1 });
+            return 501;
+        }
+        catch (EntryPointNotFoundException)
+        {
+        }
+
         return 100;
     }
 }
index d1f6e73..4c43089 100644 (file)
   }
 }
 
+.class interface public abstract auto ansi IReabstractor implements IFoo
+{
+  .method public hidebysig newslot virtual abstract final instance int32 DefaultMethod(int32) cil managed
+  {
+    .override IFoo::DefaultMethod
+  }
+}
+
 .class interface public abstract auto ansi IFoo`1<T>
 {
   .method public hidebysig newslot virtual instance valuetype [mscorlib]System.RuntimeTypeHandle DefaultMethod() cil managed
   }
 }
 
+.class public auto ansi beforefieldinit Reabstractor
+       extends [mscorlib]System.Object
+       implements IReabstractor
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
 .class public sequential ansi sealed beforefieldinit ValueFooer
        extends [mscorlib]System.ValueType
        implements IFoo, class IFoo`1<class Fooer>