Finalize override lookup algorithm (#12753)
authorYi Zhang (CLR) <yizhang82@users.noreply.github.com>
Tue, 15 Aug 2017 22:55:10 +0000 (15:55 -0700)
committerGitHub <noreply@github.com>
Tue, 15 Aug 2017 22:55:10 +0000 (15:55 -0700)
* first cut of multiple candidates

* new positive case for diamond shape
* Add proper missing implementation detection and positive diamond shape scenario support. Untested

* fix bug and get test to pass

* Add more diamond shape pos/neg tests

* Fix bad I8

* Add GVM tests to diamond shape

* GVM working

* Change test case to better match diamondshape scenario

* Fix MethodImpl::Iterator::GetMethodDesc and optimize no-method impl scenario

* Update methodimpl test to include multiple slots and fix a bug

* Temporarily disable incremental build in this branch

* Update methodimpl to include multiple methodipml overriding scenario. This is triggering some bugs and need to investigate

* Fix a buffer overrrun bug with interface methodimpl

* Update after self-review

* Add proper methodImpl validation for methods

* Address feedback. Refactoring pending

13 files changed:
CMakeLists.txt
src/dlls/mscorrc/mscorrc.rc
src/dlls/mscorrc/resource.h
src/vm/methodimpl.cpp
src/vm/methodimpl.h
src/vm/methodtable.cpp
src/vm/methodtable.h
src/vm/methodtablebuilder.cpp
src/vm/methodtablebuilder.h
tests/src/Loader/classloader/DefaultInterfaceMethods/diamondshape/diamondshape.cs
tests/src/Loader/classloader/DefaultInterfaceMethods/diamondshape/diamondshape.il
tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.cs
tests/src/Loader/classloader/DefaultInterfaceMethods/methodimpl/methodimpl.il

index 38461ac..ff21c69 100644 (file)
@@ -344,6 +344,10 @@ if (WIN32)
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /PDBCOMPRESS")
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:1572864")
 
+  # Temporarily disable incremental link due to incremental linking CFG bug crashing crossgen. 
+  # See https://github.com/dotnet/coreclr/issues/12592
+  set(NO_INCREMENTAL_LINKER_FLAGS "/INCREMENTAL:NO")  
+
   # Debug build specific flags
   set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "/NOVCFEATURE ${NO_INCREMENTAL_LINKER_FLAGS}")
   set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${NO_INCREMENTAL_LINKER_FLAGS}")
index 8dd29ae..7049c42 100644 (file)
@@ -1252,6 +1252,8 @@ BEGIN
     IDS_CLASSLOAD_MI_VIRTUALMISMATCH        "Method '%3' on type '%1' from assembly '%2' tried to implement a method declaration with a different virtual state."
     IDS_CLASSLOAD_MI_MUSTBEVIRTUAL          "Method '%3' on type '%1' from assembly '%2' must be virtual to implement a method on an interface or super type."
     IDS_CLASSLOAD_MI_BAD_SIG                "Type '%1' from assembly '%2' contains an invalid method implementation signature."
+    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 more than one incompatible interface method overriding this 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 205445a..39bc8fa 100644 (file)
 #define IDS_EE_SIMD_PARTIAL_TRUST_DISALLOWED    0x1ac4
 #define IDS_IBC_MISSING_EXTERNAL_TYPE           0x1ac5
 #define IDS_IBC_MISSING_EXTERNAL_METHOD         0x1ac6
+#define IDS_CLASSLOAD_MI_FINAL_IMPL             0x1ac7
+#define IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE        0x1ac8
 
 #define BFA_INVALID_FILE_TOKEN                  0x2000
 #define BFA_INVALID_TOKEN_TYPE                  0x2001
index c685e1c..b3f8451 100644 (file)
@@ -72,6 +72,20 @@ PTR_MethodDesc MethodImpl::FindMethodDesc(DWORD slot, PTR_MethodDesc defaultRetu
         return defaultReturn;
     }
 
+    return GetMethodDesc(slotIndex, defaultReturn);
+}
+
+PTR_MethodDesc MethodImpl::GetMethodDesc(DWORD slotIndex, PTR_MethodDesc defaultReturn)
+{
+    CONTRACTL
+    {
+        if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
+        if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
+        if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); }
+        MODE_ANY;
+    }
+    CONTRACTL_END
+
     DPTR(RelativePointer<PTR_MethodDesc>) pRelPtrForSlot = GetImpMDsNonNull();
     // The method descs are not offset by one
     TADDR base = dac_cast<TADDR>(pRelPtrForSlot) + slotIndex * sizeof(RelativePointer<MethodDesc *>);
index 453f4cc..ce915ea 100644 (file)
@@ -47,7 +47,7 @@ public:
         inline WORD GetSlot()
             { WRAPPER_NO_CONTRACT; CONSISTENCY_CHECK(IsValid()); _ASSERTE(FitsIn<WORD>(m_pImpl->GetSlots()[m_iCur])); return static_cast<WORD>(m_pImpl->GetSlots()[m_iCur]); }
         inline MethodDesc *GetMethodDesc()
-            { WRAPPER_NO_CONTRACT; return m_pImpl->FindMethodDesc(GetSlot(), (PTR_MethodDesc) m_pMD); }
+            { WRAPPER_NO_CONTRACT; return m_pImpl->GetMethodDesc(m_iCur, (PTR_MethodDesc) m_pMD); }
     };
 #endif // !DACCESS_COMPILE
 
@@ -129,6 +129,9 @@ public:
     // Returns the method desc for the replaced slot;
     PTR_MethodDesc FindMethodDesc(DWORD slot, PTR_MethodDesc defaultReturn);
 
+    // Returns the method desc for the slot index;
+    PTR_MethodDesc GetMethodDesc(DWORD slotIndex, PTR_MethodDesc defaultReturn);
+
 private:
     static const DWORD INVALID_INDEX = (DWORD)(-1);
     DWORD FindSlotIndex(DWORD slot);
index 404d903..e27d764 100644 (file)
@@ -6903,7 +6903,7 @@ MethodTable::FindDispatchImpl(
                 // See if we can find a default method from one of the implemented interfaces 
                 //
                 MethodDesc *pDefaultMethod = NULL;
-                if (FindDefaultMethod(
+                if (FindDefaultInterfaceImplementation(
                     pIfcMD,     // the interface method being resolved
                     pIfcMT,     // the interface being resolved
                     &pDefaultMethod))
@@ -6943,7 +6943,46 @@ MethodTable::FindDispatchImpl(
 }
 
 #ifndef DACCESS_COMPILE
-BOOL MethodTable::FindDefaultMethod(
+
+struct MatchCandidate
+{
+    MethodTable *pMT;
+    MethodDesc *pMD;
+};
+
+void ThrowExceptionForConflictingOverride(
+    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(
+        kNotSupportedException,
+        IDS_CLASSLOAD_AMBIGUOUS_OVERRIDE,
+        strInterfaceName,
+        strMethodName,
+        strTargetClassName,
+        assemblyName);
+}
+
+// Find the default interface implementation method for interface dispatch
+// It is either the interface method with default interface method implementation, 
+// or an most specific interface with an explicit methodimpl overriding the method
+BOOL MethodTable::FindDefaultInterfaceImplementation(
     MethodDesc *pInterfaceMD,
     MethodTable *pInterfaceMT,
     MethodDesc **ppDefaultMethod
@@ -6960,20 +6999,17 @@ BOOL MethodTable::FindDefaultMethod(
         POSTCONDITION(!RETVAL || (*ppDefaultMethod) != nullptr);
     } CONTRACT_END;
 
-    //
-    // Find best candidate
-    //
     InterfaceMapIterator it = this->IterateInterfaceMap();
-    MethodTable *pBestCandidateMT = NULL;
-    MethodDesc  *pBestCandidateMD = NULL;
-
 
+    CQuickArray<MatchCandidate> candidates;
+    int candidatesCount = 0;
+    candidates.AllocThrows(this->GetNumInterfaces());
+    
     //
     // Walk interface from derived class to parent class
-    //
-    // @DIM_TODO - This is a first cut naive implementation for prototyping and flush out other 
-    // implementation issues without worrying too much about ordering/checks
-    // Once CLR/C# team has agreed on the right order, we'll replace this with the real deal
+    // We went with a straight-forward implementation as in most cases the number of interfaces are small
+    // and the result of the interface dispatch are already cached. If there are significant usage of default
+    // interface methods in highly complex interface hierarchies we can revisit this
     //
     MethodTable *pMT = this;
     while (pMT != NULL)
@@ -7006,31 +7042,52 @@ BOOL MethodTable::FindDefaultMethod(
                 {
                     if (pCurMT->HasSameTypeDefAs(pInterfaceMT))
                     {
-                        // Generic variance match
+                        // Generic variance match - we'll instantiate pCurMD with the right type arguments later
                         pCurMD = pInterfaceMD;
                     }
                     else
                     {
                         //
-                        // Parent interface - search for an methodimpl for explicit override
+                        // A more specific interface - search for an methodimpl for explicit override
                         // Implicit override in default interface methods are not allowed
                         //
                         MethodIterator methodIt(pCurMT);
                         for (; methodIt.IsValid(); methodIt.Next())
                         {
                             MethodDesc *pMD = methodIt.GetMethodDesc();
-                            if (pMD->IsVirtual() && !pMD->IsAbstract() && pMD->IsMethodImpl())
+                            int targetSlot = pInterfaceMD->GetSlot();
+
+                            if (pMD->IsMethodImpl())
                             {
                                 MethodImpl::Iterator it(pMD);
-                                while (it.IsValid())
+                                for (; it.IsValid(); it.Next())
                                 {
-                                    if (it.GetMethodDesc() == pInterfaceMD)
+                                    MethodDesc *pDeclMD = it.GetMethodDesc();
+
+                                    if (pDeclMD->GetSlot() != targetSlot)
+                                        continue;
+
+                                    MethodTable *pDeclMT = pDeclMD->GetMethodTable();
+                                    if (pDeclMT->ContainsGenericVariables())
                                     {
+                                        TypeHandle thInstDeclMT = ClassLoader::LoadGenericInstantiationThrowing(
+                                            pDeclMT->GetModule(),
+                                            pDeclMT->GetCl(),
+                                            pCurMT->GetInstantiation());
+                                        MethodTable *pInstDeclMT = thInstDeclMT.GetMethodTable();
+                                        if (pInstDeclMT == pInterfaceMT)
+                                        {
+                                            // This is a matching override. We'll instantiate pCurMD later
+                                            pCurMD = pMD;
+                                            break;
+                                        }
+                                    }
+                                    else if (pDeclMD == pInterfaceMD)
+                                    {
+                                        // Exact match override 
                                         pCurMD = pMD;
                                         break;
                                     }
-
-                                    it.Next();
                                 } 
                             }
                         }
@@ -7059,24 +7116,65 @@ BOOL MethodTable::FindDefaultMethod(
                         );
                     }
 
-                    if (pBestCandidateMT != pCurMT)
-                    {                    
-                        if (pBestCandidateMT == NULL ||                         // first time
-                            pCurMT->CanCastToInterface(pBestCandidateMT))       // Prefer "more specific"" interface
+                    bool needToInsert = true;
+                    bool seenMoreSpecific = false;
+
+                    // We need to maintain the invariant that the candidates are always the most specific
+                    // in all path scaned so far. There might be multiple incompatible candidates 
+                    for (int i = 0; i < candidatesCount; ++i)
+                    {
+                        MethodTable *pCandidateMT = candidates[i].pMT;
+                        if (pCandidateMT == NULL)
+                            continue;
+
+                        if (pCandidateMT == pCurMT)
                         {
-                            // This is a better match
-                            pBestCandidateMT = pCurMT;
-                            pBestCandidateMD = pCurMD;
+                            // A dup - we are done
+                            needToInsert = false;
+                            break;
                         }
-                        else
+
+                        if (pCurMT->CanCastToInterface(pCandidateMT))
                         {
-                            if (!pBestCandidateMT->CanCastToInterface(pCurMT))
+                            // pCurMT is a more specific choice than IFoo/IBar both overrides IBlah :
+                            //         /--> IFoo ---\ 
+                            // pCurMT -              -->IBlah
+                            //         \--> IBar ---/
+                            // Only update first entry IFoo and null out IBar
+                            if (!seenMoreSpecific)
+                            {
+                                seenMoreSpecific = true;
+                                candidates[i].pMT = pCurMT;
+                                candidates[i].pMD = pCurMD;
+                            }
+                            else
                             {
-                                // not good. we have a conflict 
-                                COMPlusThrow(kNotSupportedException);
+                                candidates[i].pMT = NULL;
+                                candidates[i].pMD = NULL;
                             }
+
+                            needToInsert = false;
+                        }
+                        else if (pCandidateMT->CanCastToInterface(pCurMT))
+                        {
+                            // pCurMT is less specific - we don't need to scan more entries as this entry can
+                            // represent pCurMT (other entries are incompatible with pCurMT)
+                            needToInsert = false;
+                            break;
+                        }
+                        else
+                        {
+                            // pCurMT is incompatible - keep scanning 
                         }
                     }
+                    
+                    if (needToInsert)
+                    {
+                        ASSERT(candidatesCount < candidates.Size());
+                        candidates[candidatesCount].pMT = pCurMT;
+                        candidates[candidatesCount].pMD = pCurMD;
+                        candidatesCount++;
+                    }
                 }
 
                 it.Next();
@@ -7086,6 +7184,25 @@ BOOL MethodTable::FindDefaultMethod(
         pMT = pParentMT;
     }
 
+    // scan to see if there are any conflicts
+    MethodTable *pBestCandidateMT = NULL;
+    MethodDesc *pBestCandidateMD = NULL;
+    for (int i = 0; i < candidatesCount; ++i)
+    {
+        if (candidates[i].pMT == NULL)
+            continue;
+
+        if (pBestCandidateMT == NULL)
+        {
+            pBestCandidateMT = candidates[i].pMT;
+            pBestCandidateMD = candidates[i].pMD;
+        }
+        else if (pBestCandidateMT != candidates[i].pMT)
+        {
+            ThrowExceptionForConflictingOverride(this, pInterfaceMT, pInterfaceMD);
+        }
+    }
+
     if (pBestCandidateMD != NULL)
     {
         *ppDefaultMethod = pBestCandidateMD;
index 11296b0..118a883 100644 (file)
@@ -2487,7 +2487,7 @@ public:
 
 
 #ifndef DACCESS_COMPILE
-    BOOL FindDefaultMethod(
+    BOOL FindDefaultInterfaceImplementation(
         MethodDesc *pInterfaceMD,
         MethodTable *pObjectMT,
         MethodDesc **ppDefaultMethod);
index 2f76d7f..3a738df 100644 (file)
@@ -1622,6 +1622,10 @@ MethodTableBuilder::BuildMethodTableThrowing(
 
     if (IsInterface())
     {
+        //
+        // We need to process/place method impls for default interface method overrides.
+        // We won't build dispatch map for interfaces, though.
+        //
         ProcessMethodImpls();
         PlaceMethodImpls();
     }
@@ -4786,6 +4790,12 @@ VOID MethodTableBuilder::TestMethodImpl(
         BuildMethodTableThrowException(IDS_CLASSLOAD_MI_FINAL_DECL);
     }
 
+    // Interface method body that has methodimpl should always be final
+    if (IsInterface() && !IsMdFinal(dwImplAttrs))
+    {
+        BuildMethodTableThrowException(IDS_CLASSLOAD_MI_FINAL_IMPL);
+    }
+
     // Since MethodImpl's do not affect the visibility of the Decl method, there's
     // no need to check.
 
@@ -5568,6 +5578,9 @@ MethodTableBuilder::ProcessMethodImpls()
 {
     STANDARD_VM_CONTRACT;
 
+    if (bmtMethod->dwNumberMethodImpls == 0)
+        return;
+
     HRESULT hr = S_OK;
 
     DeclaredMethodIterator it(*this);
@@ -6159,9 +6172,12 @@ MethodTableBuilder::PlaceMethodImpls()
     }
 
     // Allocate some temporary storage. The number of overrides for a single method impl
-    // cannot be greater then the number of vtable slots.
-    DWORD * slots = new (&GetThread()->m_MarshalAlloc) DWORD[bmtVT->cVirtualSlots];
-    RelativePointer<MethodDesc *> * replaced = new (&GetThread()->m_MarshalAlloc) RelativePointer<MethodDesc*>[bmtVT->cVirtualSlots];
+    // cannot be greater then the number of vtable slots for classes. But for interfaces
+    // it might contain overrides for other interface methods.
+    DWORD dwMaxSlotSize = IsInterface() ? bmtMethod->dwNumberMethodImpls : bmtVT->cVirtualSlots;
+
+    DWORD * slots = new (&GetThread()->m_MarshalAlloc) DWORD[dwMaxSlotSize];
+    RelativePointer<MethodDesc *> * replaced = new (&GetThread()->m_MarshalAlloc) RelativePointer<MethodDesc*>[dwMaxSlotSize];
 
     DWORD iEntry = 0;
     bmtMDMethod * pCurImplMethod = bmtMethodImpl->GetImplementationMethod(iEntry);
@@ -6179,7 +6195,8 @@ MethodTableBuilder::PlaceMethodImpls()
         // (declaration is on this type) or a method desc.
         bmtMethodHandle hDeclMethod = bmtMethodImpl->GetDeclarationMethod(iEntry);
         if(hDeclMethod.IsMDMethod())
-        {   // The declaration is on the type being built
+        {   
+            // The declaration is on the type being built
             bmtMDMethod * pCurDeclMethod = hDeclMethod.AsMDMethod();
 
             mdToken mdef = pCurDeclMethod->GetMethodSignature().GetToken();
@@ -6190,22 +6207,25 @@ MethodTableBuilder::PlaceMethodImpls()
 
             if (IsInterface())
             {
-                // We implement this slot, record it
-                slots[slotIndex] = pCurDeclMethod->GetSlotIndex();
-                replaced[slotIndex].SetValue(pCurDeclMethod->GetMethodDesc());
-
-                // increment the counter
-                slotIndex++;
+                // Throws
+                PlaceInterfaceDeclarationOnInterface(
+                    hDeclMethod,
+                    pCurImplMethod,
+                    slots,              // Adds override to the slot and replaced arrays.
+                    replaced,
+                    &slotIndex,
+                    dwMaxSlotSize);     // Increments count                
             }
             else
             {
                 // Throws
-                PlaceLocalDeclaration(
+                PlaceLocalDeclarationOnClass(
                     pCurDeclMethod,
                     pCurImplMethod,
-                    slots,             // Adds override to the slot and replaced arrays.
+                    slots,              // Adds override to the slot and replaced arrays.
                     replaced,
-                    &slotIndex);       // Increments count
+                    &slotIndex,
+                    dwMaxSlotSize);     // Increments count
             }
         }
         else
@@ -6214,12 +6234,14 @@ MethodTableBuilder::PlaceMethodImpls()
 
             if (IsInterface())
             {
-                // We implement this slot, record it
-                slots[slotIndex] = pCurDeclMethod->GetSlotIndex();
-                replaced[slotIndex].SetValue(pCurDeclMethod->GetMethodDesc());
-
-                // increment the counter
-                slotIndex++;
+                // Throws
+                PlaceInterfaceDeclarationOnInterface(
+                    hDeclMethod,
+                    pCurImplMethod,
+                    slots,              // Adds override to the slot and replaced arrays.
+                    replaced,
+                    &slotIndex,
+                    dwMaxSlotSize);     // Increments count     
             }
             else
             {
@@ -6227,22 +6249,20 @@ MethodTableBuilder::PlaceMethodImpls()
                 if (pCurDeclMethod->GetOwningType()->IsInterface())
                 {
                     // Throws
-                    PlaceInterfaceDeclaration(
+                    PlaceInterfaceDeclarationOnClass(
                         pCurDeclMethod,
-                        pCurImplMethod,
-                        slots,
-                        replaced,
-                        &slotIndex);     // Increments count
+                        pCurImplMethod);
                 }
                 else
                 {
                     // Throws
-                    PlaceParentDeclaration(
+                    PlaceParentDeclarationOnClass(
                         pCurDeclMethod,
                         pCurImplMethod,
                         slots,
                         replaced,
-                        &slotIndex);        // Increments count
+                        &slotIndex,
+                        dwMaxSlotSize);        // Increments count
                 }
             }
         }
@@ -6250,7 +6270,8 @@ MethodTableBuilder::PlaceMethodImpls()
         iEntry++;
 
         if(iEntry == bmtMethodImpl->pIndex)
-        {   // We hit the end of the list so dump the current data and leave
+        {
+            // We hit the end of the list so dump the current data and leave
             WriteMethodImplData(pCurImplMethod, slotIndex, slots, replaced);
             break;
         }
@@ -6259,7 +6280,8 @@ MethodTableBuilder::PlaceMethodImpls()
             bmtMDMethod * pNextImplMethod = bmtMethodImpl->GetImplementationMethod(iEntry);
 
             if (pNextImplMethod != pCurImplMethod)
-            {   // If we're moving on to a new body, dump the current data and reset the counter
+            {
+                // If we're moving on to a new body, dump the current data and reset the counter
                 WriteMethodImplData(pCurImplMethod, slotIndex, slots, replaced);
                 slotIndex = 0;
             }
@@ -6302,6 +6324,8 @@ MethodTableBuilder::WriteMethodImplData(
         {
             // If we are currently builting an interface, the slots here has no meaning and we can skip it
             // Sort the two arrays in slot index order
+            // This is required in MethodImpl::FindSlotIndex and MethodImpl::Iterator as we'll be using 
+            // binary search later
             for (DWORD i = 0; i < cSlots; i++)
             {
                 int min = i;
@@ -6335,12 +6359,13 @@ MethodTableBuilder::WriteMethodImplData(
 
 //*******************************************************************************
 VOID
-MethodTableBuilder::PlaceLocalDeclaration(
+MethodTableBuilder::PlaceLocalDeclarationOnClass(
     bmtMDMethod * pDecl, 
     bmtMDMethod * pImpl, 
     DWORD *       slots, 
     RelativePointer<MethodDesc *> * replaced,
-    DWORD *       pSlotIndex)
+    DWORD *       pSlotIndex,
+    DWORD         dwMaxSlotSize)
 {
     CONTRACTL
     {
@@ -6395,20 +6420,18 @@ MethodTableBuilder::PlaceLocalDeclaration(
         pImpl);
 
     // We implement this slot, record it
+    ASSERT(*pSlotIndex < dwMaxSlotSize);
     slots[*pSlotIndex] = pDecl->GetSlotIndex();
     replaced[*pSlotIndex].SetValue(pDecl->GetMethodDesc());
 
     // increment the counter
     (*pSlotIndex)++;
-} // MethodTableBuilder::PlaceLocalDeclaration
+} // MethodTableBuilder::PlaceLocalDeclarationOnClass
 
 //*******************************************************************************
-VOID MethodTableBuilder::PlaceInterfaceDeclaration(
+VOID MethodTableBuilder::PlaceInterfaceDeclarationOnClass(
     bmtRTMethod *     pDecl,
-    bmtMDMethod *     pImpl,
-    DWORD*            slots,
-    RelativePointer<MethodDesc *> *      replaced,
-    DWORD*            pSlotIndex)
+    bmtMDMethod *     pImpl)
 {
     CONTRACTL {
         STANDARD_VM_CHECK;
@@ -6504,16 +6527,60 @@ VOID MethodTableBuilder::PlaceInterfaceDeclaration(
         }
     }
 #endif //_DEBUG
-} // MethodTableBuilder::PlaceInterfaceDeclaration
+} // MethodTableBuilder::PlaceInterfaceDeclarationOnClass
+
+//*******************************************************************************
+VOID MethodTableBuilder::PlaceInterfaceDeclarationOnInterface(
+    bmtMethodHandle hDecl, 
+    bmtMDMethod   *pImpl, 
+    DWORD *       slots, 
+    RelativePointer<MethodDesc *> * replaced,
+    DWORD *       pSlotIndex,
+    DWORD         dwMaxSlotSize)
+{
+    CONTRACTL {
+        STANDARD_VM_CHECK;
+        PRECONDITION(CheckPointer(pImpl));
+        PRECONDITION(IsInterface());
+        PRECONDITION(hDecl.GetMethodDesc()->IsInterface());
+    } CONTRACTL_END;
+
+    MethodDesc *  pDeclMD = hDecl.GetMethodDesc();
+
+    if (!bmtProp->fNoSanityChecks)
+    {
+        ///////////////////////////////
+        // Verify the signatures match
+
+        MethodImplCompareSignatures(
+            hDecl,
+            bmtMethodHandle(pImpl),
+            IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_INTERFACE_METHOD_IMPL);
+
+        ///////////////////////////////
+        // Validate the method impl.
+
+        TestMethodImpl(hDecl, bmtMethodHandle(pImpl));
+    }
+
+    // We implement this slot, record it
+    ASSERT(*pSlotIndex < dwMaxSlotSize);
+    slots[*pSlotIndex] = hDecl.GetSlotIndex();
+    replaced[*pSlotIndex].SetValue(pDeclMD);
+
+    // increment the counter
+    (*pSlotIndex)++;
+} // MethodTableBuilder::PlaceInterfaceDeclarationOnInterface
 
 //*******************************************************************************
 VOID
-MethodTableBuilder::PlaceParentDeclaration(
+MethodTableBuilder::PlaceParentDeclarationOnClass(
     bmtRTMethod * pDecl, 
     bmtMDMethod * pImpl, 
     DWORD *       slots, 
     RelativePointer<MethodDesc *> * replaced,
-    DWORD *       pSlotIndex)
+    DWORD *       pSlotIndex,
+    DWORD         dwMaxSlotSize)
 {
     CONTRACTL {
         STANDARD_VM_CHECK;
@@ -6556,12 +6623,13 @@ MethodTableBuilder::PlaceParentDeclaration(
         pImpl);
 
     // We implement this slot, record it
+    ASSERT(*pSlotIndex < dwMaxSlotSize);
     slots[*pSlotIndex] = pDeclMD->GetSlot();
     replaced[*pSlotIndex].SetValue(pDeclMD);
 
     // increment the counter
     (*pSlotIndex)++;
-} // MethodTableBuilder::PlaceParentDeclaration
+} // MethodTableBuilder::PlaceParentDeclarationOnClass
 
 //*******************************************************************************
 // This will validate that all interface methods that were matched during
@@ -10783,6 +10851,45 @@ MethodTableBuilder::SetupMethodTable2(
 #pragma warning(pop)
 #endif
 
+// Returns true if there is at least one default implementation for this interface method
+// We don't care about conflicts at this stage in order to avoid impact type load performance
+BOOL MethodTableBuilder::HasDefaultInterfaceImplementation(MethodDesc *pDeclMD)
+{
+    STANDARD_VM_CONTRACT;
+
+    // If the interface method is already non-abstract, we are done
+    if (pDeclMD->IsDefaultInterfaceMethod())
+        return TRUE;
+
+    MethodTable *pDeclMT = pDeclMD->GetMethodTable();
+
+    // Otherwise, traverse the list of interfaces and see if there is at least one override 
+    bmtInterfaceInfo::MapIterator intIt = bmtInterface->IterateInterfaceMap();
+    for (; !intIt.AtEnd(); intIt.Next())
+    {
+        MethodTable *pIntfMT = intIt->GetInterfaceType()->GetMethodTable();
+        if (pIntfMT->GetClass()->ContainsMethodImpls() && pIntfMT->CanCastToInterface(pDeclMT))
+        {
+            MethodTable::MethodIterator methodIt(pIntfMT);
+            for (; methodIt.IsValid(); methodIt.Next())
+            {
+                MethodDesc *pMD = methodIt.GetMethodDesc();
+                if (pMD->IsMethodImpl())
+                {
+                    MethodImpl::Iterator it(pMD);
+                    while (it.IsValid())
+                    {
+                        if (it.GetMethodDesc() == pDeclMD)
+                            return TRUE;
+                    }
+                }
+            }
+        }
+    }
+
+    return FALSE;
+}
+
 void MethodTableBuilder::VerifyVirtualMethodsImplemented(MethodTable::MethodData * hMTData)
 {
     STANDARD_VM_CONTRACT;
@@ -10855,13 +10962,13 @@ void MethodTableBuilder::VerifyVirtualMethodsImplemented(MethodTable::MethodData
             MethodTable::MethodIterator it(hData);
             for (; it.IsValid() && it.IsVirtual(); it.Next())
             {
-                // @DIM_TODO - What is the right level of check if the interface itself does not have default implementation
-                // but a derived interface do
-                // if (it.GetTarget().IsNull())
-                // {
-                //    MethodDesc *pMD = it.GetDeclMethodDesc();
-                //    BuildMethodTableThrowException(IDS_CLASSLOAD_NOTIMPLEMENTED, pMD->GetNameOnNonArrayClass());
-                // }
+                if (it.GetTarget().IsNull())
+                {
+                    MethodDesc *pMD = it.GetDeclMethodDesc();
+
+                    if (!HasDefaultInterfaceImplementation(pMD))
+                        BuildMethodTableThrowException(IDS_CLASSLOAD_NOTIMPLEMENTED, pMD->GetNameOnNonArrayClass());
+                }
             }
         }
     }
index e5043cf..8c838d3 100644 (file)
@@ -2739,32 +2739,42 @@ private:
     // --------------------------------------------------------------------------------------------
     // Places a methodImpl pair where the decl is declared by the type being built.
     VOID
-    PlaceLocalDeclaration(
+    PlaceLocalDeclarationOnClass(
         bmtMDMethod *    pDecl,
         bmtMDMethod *    pImpl,
         DWORD*           slots,
         RelativePointer<MethodDesc *> *     replaced,
-        DWORD*           pSlotIndex);
+        DWORD*           pSlotIndex,
+        DWORD            dwMaxSlotSize);
 
     // --------------------------------------------------------------------------------------------
     // Places a methodImpl pair where the decl is declared by a parent type.
     VOID
-    PlaceParentDeclaration(
+    PlaceParentDeclarationOnClass(
         bmtRTMethod *     pDecl,
         bmtMDMethod *     pImpl,
         DWORD*            slots,
         RelativePointer<MethodDesc *> *      replaced,
-        DWORD*            pSlotIndex);
+        DWORD*            pSlotIndex,
+        DWORD             dwMaxSlotSize);
 
     // --------------------------------------------------------------------------------------------
-    // Places a methodImpl pair where the decl is declared by an interface.
+    // Places a methodImpl pair on a class where the decl is declared by an interface.
     VOID
-    PlaceInterfaceDeclaration(
+    PlaceInterfaceDeclarationOnClass(
         bmtRTMethod *     pDecl,
-        bmtMDMethod *     pImpl,
+        bmtMDMethod *     pImpl);
+
+    // --------------------------------------------------------------------------------------------
+    // Places a methodImpl pair on an interface where the decl is declared by an interface.
+    VOID
+    PlaceInterfaceDeclarationOnInterface(
+        bmtMethodHandle   hDecl, 
+        bmtMDMethod *     pImpl, 
         DWORD*            slots,
         RelativePointer<MethodDesc *> *      replaced,
-        DWORD*            pSlotIndex);
+        DWORD*            pSlotIndex,
+        DWORD             dwMaxSlotSize);
 
     // --------------------------------------------------------------------------------------------
     // This will validate that all interface methods that were matched during
@@ -2846,6 +2856,7 @@ private:
     VOID HandleGCForValueClasses(
         MethodTable **);
 
+    BOOL HasDefaultInterfaceImplementation(MethodDesc *pIntfMD);
     VOID VerifyVirtualMethodsImplemented(MethodTable::MethodData * hMTData);
 
     VOID CheckForTypeEquivalence(
index f6c7786..3ff3760 100644 (file)
@@ -48,9 +48,113 @@ class FooClass : IFoo2, IFooEx
     }
 }
 
+interface I1
+{
+    int Func(int a);    
+}
+
+interface I2 : I1
+{
+    // int I1.Func(int a) { return a + 2; }
+}
+
+interface I3 : I1
+{
+    // int I1.Func(int a) { return a + 3; }
+}
+
+interface I4 : I2, I3
+{
+    // int I1.Func(int a) { return a + 4; }
+}
+
+class I4Class : I4
+{
+    // @REMOVE
+    int I1.Func(int a)
+    {
+        Console.WriteLine("At I4Class.Func");
+        return a + 4;
+    }        
+}
+
+interface I5: I1 
+{
+    // int I1.Func(int a) { return a + 5; }
+}
+
+interface I6: I1 
+{
+    // int I1.Func(int a) { return a + 6; }
+}
+
+interface I7: I5, I6
+{
+    // int I1.Func(int a) { return a + 7; }
+}
+
+interface I8: I4, I7
+{
+    // int I1.Func(int a) { return a + 8; }
+}
+
+class I47Class: I4, I7
+{
+    // @REMOVE
+    int I1.Func(int a)
+    {
+        Console.WriteLine("At I4Class.Func");
+        return a + 8;
+    }            
+
+}
+
+class I8Class: I8
+{
+    // @REMOVE
+    int I1.Func(int a)
+    {
+        Console.WriteLine("At I4Class.Func");
+        return a + 8;
+    }            
+}
+
+interface GI1<T>
+{
+    int Func<S>(out Type[] types); 
+}
+
+interface GI2<T> : GI1<T>
+{
+    // int GI1<T>.Func<S>(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 2; }  
+
+} 
+
+interface GI3<T> : GI1<T>
+{
+    // int GI1<T>.Func<S>(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 3; }  
+} 
+
+interface GI4<T> : GI2<T>, GI3<T>
+{
+    // int GI1<T>.Func<S>(out Type[] types) { Console.WriteLine(typeof(T) + ", "typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; }  
+} 
+
+class GI23Class<T>: GI2<T>, GI3<T>
+{
+    // @REMOVE
+    int GI1<T>.Func<S>(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; }  
+}
+
+class GI4Class<T>: GI4<T>
+{
+    // @REMOVE
+    int GI1<T>.Func<S>(out Type[] types) { Console.WriteLine(typeof(T) + ", " + typeof(S) + ", GI1Class"); types = new Type[] { typeof(T), typeof(S) }; return 4; }  
+}
+
 class Program
 {
-    public static int Main()
+    public static void Negative()
     {
         FooClass fooObj = new FooClass();
         IFoo foo = (IFoo) fooObj;
@@ -59,12 +163,69 @@ class Program
         try
         {
              foo.Foo(10);
-             Test.Assert(false, "Expecting exception");
+             Test.Assert(false, "Expecting exception on Foo");
+        }
+        catch(Exception ex)
+        {
+            Console.WriteLine("Exception caught: " + ex.ToString());
         }
-        catch(Exception)
+
+        I47Class i47Class = new I47Class();
+        I1 i1 = (I1) i47Class;
+        Console.WriteLine("Calling I1.Func on I47Class - expecting exception");
+        try
+        {
+            i1.Func(10);
+            Test.Assert(false, "Expecting exception on I47Class");
+        }
+        catch(Exception ex)
+        {
+            Console.WriteLine("Exception caught: " + ex.ToString());
+        }
+
+        var gi23Class = new GI23Class<object>();
+        GI1<object> gi1 = (GI1<object>) gi23Class;
+        Console.WriteLine("Calling GI1<T>.Func on GI23Class<S> - expecting exception");
+        try
         {
+            Type[] types;
+            gi1.Func<string>(out types);
+            Test.Assert(false, "Expecting exception on GI23Class");
         }
+        catch(Exception ex)
+        {
+            Console.WriteLine("Exception caught: " + ex.ToString());
+        }          
+    }
 
+    public static void Positive()
+    {
+        Console.WriteLine("Calling I1.Func on I4Class - expecting I4.Func");
+
+        I4Class i4Class = new I4Class();
+        I1 i1 = (I1) i4Class;
+        Test.Assert(i1.Func(10) == 14, "Expecting I1.Func to land on I4.Func");
+        
+        Console.WriteLine("Calling I1.Func on I8Class - expecting I8.Func");
+
+        I8Class i8Class = new I8Class();
+        i1 = (I1) i8Class;
+        Test.Assert(i1.Func(10) == 18, "Expecting I1.Func to land on I8.Func");
+
+        Console.WriteLine("Calling GI1.Func on GI4Class<object> - expecting GI4.Func<S>");
+
+        var gi4Class = new GI4Class<object>();
+        Type[] types;
+        var gi1 = (GI1<object>) gi4Class;
+        Test.Assert(gi1.Func<string>(out types) == 4, "Expecting GI1<T>.Func to land on GII4<T>.Func<S>");
+        Test.Assert(types[0] == typeof(object), "T must be object");
+        Test.Assert(types[1] == typeof(string), "S must be string");  
+    }
+
+    public static int Main()
+    {
+        Negative();
+        Positive();
         return Test.Ret();
     }
 }
index 82d9fa7..4173bc6 100644 (file)
 
 } // end of class FooClass
 
+.class interface private abstract auto ansi I1
+{
+  .method public hidebysig newslot abstract virtual 
+          instance int32  Func(int32 a) cil managed
+  {
+  } // end of method I1::Func
+
+} // end of class I1
+
+.class interface private abstract auto ansi I2
+       implements I1
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  I1.Func(int32 a) cil managed
+  {
+    .override I1::Func
+    // Code size       20 (0x14)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At I2.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.2
+    IL_000e:  add
+    IL_000f:  stloc.0
+    IL_0010:  br.s       IL_0012
+
+    IL_0012:  ldloc.0
+    IL_0013:  ret
+  } // end of method I2::I1.Func    
+} // end of class I2
+
+.class interface private abstract auto ansi I3
+       implements I1
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  I1.Func(int32 a) cil managed
+  {
+    .override I1::Func
+    // Code size       20 (0x14)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At I3.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.3
+    IL_000e:  add
+    IL_000f:  stloc.0
+    IL_0010:  br.s       IL_0012
+
+    IL_0012:  ldloc.0
+    IL_0013:  ret
+  } // end of method I3::I1.Func    
+} // end of class I3
+
+.class interface private abstract auto ansi I4
+       implements I2,
+                  I1,
+                  I3
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  I1.Func(int32 a) cil managed
+  {
+    .override I1::Func
+    // Code size       20 (0x14)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At I4.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.4
+    IL_000e:  add
+    IL_000f:  stloc.0
+    IL_0010:  br.s       IL_0012
+
+    IL_0012:  ldloc.0
+    IL_0013:  ret
+  } // end of method I4::I1.Func   
+} // end of class I4
+
+.class private auto ansi beforefieldinit I4Class
+       extends [mscorlib]System.Object
+       implements I4,
+                  I2,
+                  I1,
+                  I3
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method I4Class::.ctor
+
+} // end of class I4Class
+
+.class interface private abstract auto ansi I5
+       implements I1
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  I1.Func(int32 a) cil managed
+  {
+    .override I1::Func
+    // Code size       20 (0x14)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At I5.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.5
+    IL_000e:  add
+    IL_000f:  stloc.0
+    IL_0010:  br.s       IL_0012
+
+    IL_0012:  ldloc.0
+    IL_0013:  ret
+  } // end of method I5::I1.Func  
+} // end of class I5
+
+.class interface private abstract auto ansi I6
+       implements I1
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  I1.Func(int32 a) cil managed
+  {
+    .override I1::Func
+    // Code size       20 (0x14)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At I6.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.6
+    IL_000e:  add
+    IL_000f:  stloc.0
+    IL_0010:  br.s       IL_0012
+
+    IL_0012:  ldloc.0
+    IL_0013:  ret
+  } // end of method I6::I1.Func  
+} // end of class I6
+
+.class interface private abstract auto ansi I7
+       implements I5,
+                  I1,
+                  I6
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  I1.Func(int32 a) cil managed
+  {
+    .override I1::Func
+    // Code size       20 (0x14)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At I7.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.7
+    IL_000e:  add
+    IL_000f:  stloc.0
+    IL_0010:  br.s       IL_0012
+
+    IL_0012:  ldloc.0
+    IL_0013:  ret
+  } // end of method I7::I1.Func  
+} // end of class I7
+
+.class interface private abstract auto ansi I8
+       implements I4,
+                  I2,
+                  I1,
+                  I3,
+                  I7,
+                  I5,
+                  I6
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  I1.Func(int32 a) cil managed
+  {
+    .override I1::Func
+    // Code size       20 (0x14)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At I8.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.8
+    IL_000e:  add
+    IL_000f:  stloc.0
+    IL_0010:  br.s       IL_0012
+
+    IL_0012:  ldloc.0
+    IL_0013:  ret
+  } // end of method I8::I1.Func  
+} // end of class I8
+
+.class private auto ansi beforefieldinit I47Class
+       extends [mscorlib]System.Object
+       implements I4,
+                  I2,
+                  I1,
+                  I3,
+                  I7,
+                  I5,
+                  I6
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method I47Class::.ctor
+
+} // end of class I47Class
+
+.class private auto ansi beforefieldinit I8Class
+       extends [mscorlib]System.Object
+       implements I8,
+                  I4,
+                  I2,
+                  I1,
+                  I3,
+                  I7,
+                  I5,
+                  I6 
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method I8Class::.ctor
+
+} // end of class I8Class  
+
+.class interface private abstract auto ansi GI1`1<T>
+{
+  .method public hidebysig newslot abstract virtual 
+          instance int32  Func<S>([out] class [mscorlib]System.Type[]& types) cil managed
+  {
+  } // end of method GI1`1::'GI1<T>.Func' 
+
+} // end of class GI1`1
+
+.class interface private abstract auto ansi GI2`1<T>
+       implements class GI1`1<!T>
+{
+.method private hidebysig newslot virtual final 
+          instance int32  'GI1<T>.Func'<S>([out] class [mscorlib]System.Type[]& types) cil managed
+  {
+    .override  method instance int32 class GI1`1<!T>::Func<[1]>(class [mscorlib]System.Type[]&)
+    // Code size       100 (0x64)
+    .maxstack  5
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldc.i4.4
+    IL_0002:  newarr     [mscorlib]System.Object
+    IL_0007:  dup
+    IL_0008:  ldc.i4.0
+    IL_0009:  ldtoken    !T
+    IL_000e:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_0013:  stelem.ref
+    IL_0014:  dup
+    IL_0015:  ldc.i4.1
+    IL_0016:  ldstr      ", "
+    IL_001b:  stelem.ref
+    IL_001c:  dup
+    IL_001d:  ldc.i4.2
+    IL_001e:  ldtoken    !!S
+    IL_0023:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_0028:  stelem.ref
+    IL_0029:  dup
+    IL_002a:  ldc.i4.3
+    IL_002b:  ldstr      ", GI2"
+    IL_0030:  stelem.ref
+    IL_0031:  call       string [mscorlib]System.String::Concat(object[])
+    IL_0036:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_003b:  nop
+    IL_003c:  ldarg.1
+    IL_003d:  ldc.i4.2
+    IL_003e:  newarr     [mscorlib]System.Type
+    IL_0043:  dup
+    IL_0044:  ldc.i4.0
+    IL_0045:  ldtoken    !T
+    IL_004a:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_004f:  stelem.ref
+    IL_0050:  dup
+    IL_0051:  ldc.i4.1
+    IL_0052:  ldtoken    !!S
+    IL_0057:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_005c:  stelem.ref
+    IL_005d:  stind.ref
+    IL_005e:  ldc.i4.2
+    IL_005f:  stloc.0
+    IL_0060:  br.s       IL_0062
+
+    IL_0062:  ldloc.0
+    IL_0063:  ret
+  } // end of method G2`1::'GI1<T>.Func' 
+} // end of class GI2`1
+
+.class interface private abstract auto ansi GI3`1<T>
+       implements class GI1`1<!T>
+{
+.method private hidebysig newslot virtual final 
+          instance int32  'GI1<T>.Func'<S>([out] class [mscorlib]System.Type[]& types) cil managed
+  {
+    .override  method instance int32 class GI1`1<!T>::Func<[1]>(class [mscorlib]System.Type[]&)
+    // Code size       100 (0x64)
+    .maxstack  5
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldc.i4.4
+    IL_0002:  newarr     [mscorlib]System.Object
+    IL_0007:  dup
+    IL_0008:  ldc.i4.0
+    IL_0009:  ldtoken    !T
+    IL_000e:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_0013:  stelem.ref
+    IL_0014:  dup
+    IL_0015:  ldc.i4.1
+    IL_0016:  ldstr      ", "
+    IL_001b:  stelem.ref
+    IL_001c:  dup
+    IL_001d:  ldc.i4.2
+    IL_001e:  ldtoken    !!S
+    IL_0023:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_0028:  stelem.ref
+    IL_0029:  dup
+    IL_002a:  ldc.i4.3
+    IL_002b:  ldstr      ", GI3"
+    IL_0030:  stelem.ref
+    IL_0031:  call       string [mscorlib]System.String::Concat(object[])
+    IL_0036:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_003b:  nop
+    IL_003c:  ldarg.1
+    IL_003d:  ldc.i4.2
+    IL_003e:  newarr     [mscorlib]System.Type
+    IL_0043:  dup
+    IL_0044:  ldc.i4.0
+    IL_0045:  ldtoken    !T
+    IL_004a:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_004f:  stelem.ref
+    IL_0050:  dup
+    IL_0051:  ldc.i4.1
+    IL_0052:  ldtoken    !!S
+    IL_0057:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_005c:  stelem.ref
+    IL_005d:  stind.ref
+    IL_005e:  ldc.i4.3
+    IL_005f:  stloc.0
+    IL_0060:  br.s       IL_0062
+
+    IL_0062:  ldloc.0
+    IL_0063:  ret
+  } // end of method GI3`1::'GI1<T>.Func' 
+} // end of class GI3`1
+
+.class interface private abstract auto ansi GI4`1<T>
+       implements class GI2`1<!T>,
+                  class GI1`1<!T>,
+                  class GI3`1<!T>
+{
+ .method private hidebysig newslot virtual final 
+          instance int32  'GI1<T>.Func'<S>([out] class [mscorlib]System.Type[]& types) cil managed
+  {
+    .override  method instance int32 class GI1`1<!T>::Func<[1]>(class [mscorlib]System.Type[]&)
+    // Code size       100 (0x64)
+    .maxstack  5
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldc.i4.4
+    IL_0002:  newarr     [mscorlib]System.Object
+    IL_0007:  dup
+    IL_0008:  ldc.i4.0
+    IL_0009:  ldtoken    !T
+    IL_000e:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_0013:  stelem.ref
+    IL_0014:  dup
+    IL_0015:  ldc.i4.1
+    IL_0016:  ldstr      ", "
+    IL_001b:  stelem.ref
+    IL_001c:  dup
+    IL_001d:  ldc.i4.2
+    IL_001e:  ldtoken    !!S
+    IL_0023:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_0028:  stelem.ref
+    IL_0029:  dup
+    IL_002a:  ldc.i4.3
+    IL_002b:  ldstr      ", GI4"
+    IL_0030:  stelem.ref
+    IL_0031:  call       string [mscorlib]System.String::Concat(object[])
+    IL_0036:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_003b:  nop
+    IL_003c:  ldarg.1
+    IL_003d:  ldc.i4.2
+    IL_003e:  newarr     [mscorlib]System.Type
+    IL_0043:  dup
+    IL_0044:  ldc.i4.0
+    IL_0045:  ldtoken    !T
+    IL_004a:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_004f:  stelem.ref
+    IL_0050:  dup
+    IL_0051:  ldc.i4.1
+    IL_0052:  ldtoken    !!S
+    IL_0057:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_005c:  stelem.ref
+    IL_005d:  stind.ref
+    IL_005e:  ldc.i4.4
+    IL_005f:  stloc.0
+    IL_0060:  br.s       IL_0062
+
+    IL_0062:  ldloc.0
+    IL_0063:  ret
+  } // end of method GI4`1::'GI1<T>.Func' 
+} // end of class GI4`1
+
+.class private auto ansi beforefieldinit GI23Class`1<T>
+       extends [mscorlib]System.Object
+       implements class GI2`1<!T>,
+                  class GI1`1<!T>,
+                  class GI3`1<!T>
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method GI23Class`1::.ctor
+
+} // end of class GI23Class`1
+
+.class private auto ansi beforefieldinit GI4Class`1<T>
+       extends [mscorlib]System.Object
+       implements class GI4`1<!T>,
+                  class GI2`1<!T>,
+                  class GI1`1<!T>,
+                  class GI3`1<!T>
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method GI4Class`1::.ctor
+
+} // end of class GI4Class`1
+
 .class private auto ansi beforefieldinit Program
        extends [mscorlib]System.Object
 {
-  .method public hidebysig static int32  Main() cil managed
+  .method public hidebysig static void  Negative() cil managed
   {
-    .entrypoint
-    // Code size       60 (0x3c)
+    // Code size       225 (0xe1)
     .maxstack  2
     .locals init (class FooClass V_0,
              class IFoo V_1,
-             int32 V_2)
+             class I47Class V_2,
+             class I1 V_3,
+             class GI23Class`1<object> V_4,
+             class GI1`1<object> V_5,
+             class [mscorlib]System.Exception V_6,
+             class [mscorlib]System.Exception V_7,
+             class [mscorlib]System.Type[] V_8,
+             class [mscorlib]System.Exception V_9)
     IL_0000:  nop
     IL_0001:  newobj     instance void FooClass::.ctor()
     IL_0006:  stloc.0
       IL_0018:  callvirt   instance int32 IFoo::Foo(int32)
       IL_001d:  pop
       IL_001e:  ldc.i4.0
-      IL_001f:  ldstr      "Expecting exception"
+      IL_001f:  ldstr      "Expecting exception on Foo"
       IL_0024:  call       void Test::Assert(bool,
                                              string)
       IL_0029:  nop
       IL_002a:  nop
-      IL_002b:  leave.s    IL_0032
+      IL_002b:  leave.s    IL_004a
 
     }  // end .try
     catch [mscorlib]System.Exception 
     {
-      IL_002d:  pop
-      IL_002e:  nop
+      IL_002d:  stloc.s    V_6
       IL_002f:  nop
-      IL_0030:  leave.s    IL_0032
+      IL_0030:  ldstr      "Exception caught: "
+      IL_0035:  ldloc.s    V_6
+      IL_0037:  callvirt   instance string [mscorlib]System.Object::ToString()
+      IL_003c:  call       string [mscorlib]System.String::Concat(string,
+                                                                  string)
+      IL_0041:  call       void [mscorlib]System.Console::WriteLine(string)
+      IL_0046:  nop
+      IL_0047:  nop
+      IL_0048:  leave.s    IL_004a
 
     }  // end handler
-    IL_0032:  call       int32 Test::Ret()
-    IL_0037:  stloc.2
-    IL_0038:  br.s       IL_003a
+    IL_004a:  newobj     instance void I47Class::.ctor()
+    IL_004f:  stloc.2
+    IL_0050:  ldloc.2
+    IL_0051:  stloc.3
+    IL_0052:  ldstr      "Calling I1.Func on I47Class - expecting exception"
+    IL_0057:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_005c:  nop
+    .try
+    {
+      IL_005d:  nop
+      IL_005e:  ldloc.3
+      IL_005f:  ldc.i4.s   10
+      IL_0061:  callvirt   instance int32 I1::Func(int32)
+      IL_0066:  pop
+      IL_0067:  ldc.i4.0
+      IL_0068:  ldstr      "Expecting exception on I47Class"
+      IL_006d:  call       void Test::Assert(bool,
+                                             string)
+      IL_0072:  nop
+      IL_0073:  nop
+      IL_0074:  leave.s    IL_0093
 
-    IL_003a:  ldloc.2
-    IL_003b:  ret
+    }  // end .try
+    catch [mscorlib]System.Exception 
+    {
+      IL_0076:  stloc.s    V_7
+      IL_0078:  nop
+      IL_0079:  ldstr      "Exception caught: "
+      IL_007e:  ldloc.s    V_7
+      IL_0080:  callvirt   instance string [mscorlib]System.Object::ToString()
+      IL_0085:  call       string [mscorlib]System.String::Concat(string,
+                                                                  string)
+      IL_008a:  call       void [mscorlib]System.Console::WriteLine(string)
+      IL_008f:  nop
+      IL_0090:  nop
+      IL_0091:  leave.s    IL_0093
+
+    }  // end handler
+    IL_0093:  newobj     instance void class GI23Class`1<object>::.ctor()
+    IL_0098:  stloc.s    V_4
+    IL_009a:  ldloc.s    V_4
+    IL_009c:  stloc.s    V_5
+    IL_009e:  ldstr      "Calling GI1<T>.Func on GI23Class<S> - expecting ex"
+    + "ception"
+    IL_00a3:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_00a8:  nop
+    .try
+    {
+      IL_00a9:  nop
+      IL_00aa:  ldloc.s    V_5
+      IL_00ac:  ldloca.s   V_8
+      IL_00ae:  callvirt   instance int32 class GI1`1<object>::Func<string>(class [mscorlib]System.Type[]&)
+      IL_00b3:  pop
+      IL_00b4:  ldc.i4.0
+      IL_00b5:  ldstr      "Expecting exception on GI23Class"
+      IL_00ba:  call       void Test::Assert(bool,
+                                             string)
+      IL_00bf:  nop
+      IL_00c0:  nop
+      IL_00c1:  leave.s    IL_00e0
+
+    }  // end .try
+    catch [mscorlib]System.Exception 
+    {
+      IL_00c3:  stloc.s    V_9
+      IL_00c5:  nop
+      IL_00c6:  ldstr      "Exception caught: "
+      IL_00cb:  ldloc.s    V_9
+      IL_00cd:  callvirt   instance string [mscorlib]System.Object::ToString()
+      IL_00d2:  call       string [mscorlib]System.String::Concat(string,
+                                                                  string)
+      IL_00d7:  call       void [mscorlib]System.Console::WriteLine(string)
+      IL_00dc:  nop
+      IL_00dd:  nop
+      IL_00de:  leave.s    IL_00e0
+
+    }  // end handler
+    IL_00e0:  ret 
+  } // end of method Program::Negative
+
+  .method public hidebysig static void  Positive() cil managed
+  {
+    // Code size       189 (0xbd)
+    .maxstack  2
+    .locals init (class I4Class V_0,
+             class I1 V_1,
+             class I8Class V_2,
+             class GI4Class`1<object> V_3,
+             class [mscorlib]System.Type[] V_4,
+             class GI1`1<object> V_5)
+    IL_0000:  nop
+    IL_0001:  ldstr      "Calling I1.Func on I4Class - expecting I4.Func"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  newobj     instance void I4Class::.ctor()
+    IL_0011:  stloc.0
+    IL_0012:  ldloc.0
+    IL_0013:  stloc.1
+    IL_0014:  ldloc.1
+    IL_0015:  ldc.i4.s   10
+    IL_0017:  callvirt   instance int32 I1::Func(int32)
+    IL_001c:  ldc.i4.s   14
+    IL_001e:  ceq
+    IL_0020:  ldstr      "Expecting I1.Func to land on I4.Func"
+    IL_0025:  call       void Test::Assert(bool,
+                                           string)
+    IL_002a:  nop
+    IL_002b:  ldstr      "Calling I1.Func on I8Class - expecting I8.Func"
+    IL_0030:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_0035:  nop
+    IL_0036:  newobj     instance void I8Class::.ctor()
+    IL_003b:  stloc.2
+    IL_003c:  ldloc.2
+    IL_003d:  stloc.1
+    IL_003e:  ldloc.1
+    IL_003f:  ldc.i4.s   10
+    IL_0041:  callvirt   instance int32 I1::Func(int32)
+    IL_0046:  ldc.i4.s   18
+    IL_0048:  ceq
+    IL_004a:  ldstr      "Expecting I1.Func to land on I8.Func"
+    IL_004f:  call       void Test::Assert(bool,
+                                           string)
+    IL_0054:  nop
+    IL_0055:  ldstr      "Calling GI1.Func on GI4Class<object> - expecting G"
+    + "I4.Func<S>"
+    IL_005a:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_005f:  nop
+    IL_0060:  newobj     instance void class GI4Class`1<object>::.ctor()
+    IL_0065:  stloc.3
+    IL_0066:  ldloc.3
+    IL_0067:  stloc.s    V_5
+    IL_0069:  ldloc.s    V_5
+    IL_006b:  ldloca.s   V_4
+    IL_006d:  callvirt   instance int32 class GI1`1<object>::Func<string>(class [mscorlib]System.Type[]&)
+    IL_0072:  ldc.i4.4
+    IL_0073:  ceq
+    IL_0075:  ldstr      "Expecting GI1<T>.Func to land on GII4<T>.Func<S>"
+    IL_007a:  call       void Test::Assert(bool,
+                                           string)
+    IL_007f:  nop
+    IL_0080:  ldloc.s    V_4
+    IL_0082:  ldc.i4.0
+    IL_0083:  ldelem.ref
+    IL_0084:  ldtoken    [mscorlib]System.Object
+    IL_0089:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_008e:  call       bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type,
+                                                                 class [mscorlib]System.Type)
+    IL_0093:  ldstr      "T must be object"
+    IL_0098:  call       void Test::Assert(bool,
+                                           string)
+    IL_009d:  nop
+    IL_009e:  ldloc.s    V_4
+    IL_00a0:  ldc.i4.1
+    IL_00a1:  ldelem.ref
+    IL_00a2:  ldtoken    [mscorlib]System.String
+    IL_00a7:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
+    IL_00ac:  call       bool [mscorlib]System.Type::op_Equality(class [mscorlib]System.Type,
+                                                                 class [mscorlib]System.Type)
+    IL_00b1:  ldstr      "S must be string"
+    IL_00b6:  call       void Test::Assert(bool,
+                                           string)
+    IL_00bb:  nop
+    IL_00bc:  ret
+  } // end of method Program::Positive
+
+  .method public hidebysig static int32  Main() cil managed
+  {
+    .entrypoint
+    // Code size       23 (0x17)
+    .maxstack  1
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  call       void Program::Negative()
+    IL_0006:  nop
+    IL_0007:  call       void Program::Positive()
+    IL_000c:  nop
+    IL_000d:  call       int32 Test::Ret()
+    IL_0012:  stloc.0
+    IL_0013:  br.s       IL_0015
+
+    IL_0015:  ldloc.0
+    IL_0016:  ret
   } // end of method Program::Main
 
   .method public hidebysig specialname rtspecialname 
index 8f4b5d1..0d4c119 100755 (executable)
@@ -2,58 +2,291 @@ using System;
 
 interface IFoo
 {
-    int Foo(int a);
+    int Foo1(int a); // { return a + 1 };
+    int Foo2(int a); // { return a + 2 };
+    int Foo3(int a);
+    int Foo4(int a);
+    int Foo5(int a);
+    int Foo6(int a);
+    int Foo7(int a);
+    int Foo8(int a);
+    int Foo9(int a);
 }
 
-interface IBar
+interface IBar : IFoo
 {
-    int Bar(int b);
+    // @OVERRIDE
+    // IFoo.Foo1/2/3/4/5
+
+    int Bar1(int b); // { return a + 11; }
+    int Bar2(int b); // { return a + 22; } 
+    int Bar3(int b); // { return a + 33; } 
+    int Bar4(int b);
+    int Bar5(int b);
+    int Bar6(int b);
+    int Bar7(int b);
+    int Bar8(int b);
+    int Bar9(int b);
 }
 
-interface IFooBar : IFoo, IBar
+interface IBlah : IBar
 {
-    int Foo(int a);
+    // @OVERRIDE IFoo.Foo6/7/8/9
+    // @OVERRIDE IBar.Bar6/7/8/9
+    int Blah1(int c);
+    int Blah2(int c);
+    int Blah3(int c);
 }
 
-class Temp : IFoo
+class IBarImpl : IBar
 {
-    int IFoo.Foo(int a)
+    // @REMOVE all implementation
+    int IFoo.Foo1(int a)
     {
-        Console.WriteLine("At IFooBar::IFoo.Foo explicit methodimpl");
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 10;
+    }
+
+    int IFoo.Foo2(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 20;
+    }
+    int IFoo.Foo3(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
         return a + 30;
     }
+    int IFoo.Foo4(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 40;
+    }
+    int IFoo.Foo5(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 50;
+    }
+    int IFoo.Foo6(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 60;
+    }
+    int IFoo.Foo7(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 70;
+    }
+    int IFoo.Foo8(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 80;
+    }
+    int IFoo.Foo9(int a)
+    {
+        Console.WriteLine("At IIFoo.Foo1");
+        return a + 19;
+    }
+
+    int IBar.Bar1(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 110;
+    }
+
+    int IBar.Bar2(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 220;
+    }
+    int IBar.Bar3(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 330;
+    }
+    int IBar.Bar4(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 440;
+    }
+    int IBar.Bar5(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 550;
+    }
+    int IBar.Bar6(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 660;
+    }
+    int IBar.Bar7(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 770;
+    }
+    int IBar.Bar8(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 880;
+    }
+    int IBar.Bar9(int a)
+    {
+        Console.WriteLine("At IBar.Bar1");
+        return a + 990;
+    }      
 }
 
-class FooBar : IFooBar
+class IBlahImpl : IBarImpl, IBlah
 {
-    public int Foo(int a)
+    // @REMOVE all implementation
+    // @OVERRIDE IBlah2/3 with + 2220/3330
+    int IBlah.Blah1(int c)
+    {
+        Console.WriteLine("At IBlah.Blah1");
+        return c+111;
+    }
+
+    int IBlah.Blah2(int c)
     {
-        Console.WriteLine("At IFoo::Foo");
-        return a+10;            
+        Console.WriteLine("At IBlah.Blah2");
+        return c+222;
     }
 
-    public int Bar(int b)
+    int IBlah.Blah3(int c)
     {
-        Console.WriteLine("At IBar::Bar");
-        return b+20;
+        Console.WriteLine("At IBlah.Blah3");
+        return c+333;
     }
 }
 
+interface IFooBarBlah : IFoo, IBar, IBlah
+{
+    // FooBarBlah1 .override IFoo.Foo1/IBar.Bar1/IBlah.Blah1 return 1+11111
+    // FooBarBlah2 .override IFoo.Foo2/IBar.Bar2/IBlah.Blah2 return i+22222
+    // FooBarBLah345 .override IFoo.Foo345/IBar.Bar345/IBlah.Blah3 return i+33333
+}
+
+class FooBarBlahImpl : 
+    IBlahImpl,   // @REMOVE
+    IFooBarBlah
+{
+
+}
+
 class Program
 {
     public static int Main()
     {
-        FooBar fooBar = new FooBar();
-        IFoo foo = (IFoo) fooBar;
-        IBar bar = (IBar) fooBar;
+        SingleOverride();
+        MultiOverride();
+                              
+        return Test.Ret();
+    }
 
-        Console.WriteLine("Calling IFoo.Foo on FooBar - expecting IFooBar::IFoo.Bar");
-        Test.Assert(foo.Foo(10) == 40, "Calling IFoo.Foo on FooBar");
+    private static void SingleOverride()
+    {
+        IBarImpl barImpl = new IBarImpl();
+        IFoo foo = (IFoo) barImpl;
 
-        Console.WriteLine("Calling IBar.Bar on FooBar - expecting IBar::Bar");
-        Test.Assert(bar.Bar(10) == 30, "Calling IBar.Bar on FooBar");
+        Console.WriteLine("Calling IFoo.Foo methods on IBarImpl...");
 
-        return Test.Ret();
+        Test.Assert(foo.Foo1(1) == 11, "Calling IFoo.Foo1 on IBarImpl");
+        Test.Assert(foo.Foo2(2) == 22, "Calling IFoo.Foo2 on IBarImpl");
+        Test.Assert(foo.Foo3(3) == 33, "Calling IFoo.Foo3 on IBarImpl");
+        Test.Assert(foo.Foo4(4) == 44, "Calling IFoo.Foo4 on IBarImpl");
+        Test.Assert(foo.Foo5(5) == 55, "Calling IFoo.Foo5 on IBarImpl");
+        Test.Assert(foo.Foo6(0) == 6, "Calling IFoo.Foo6 on IBarImpl");
+        Test.Assert(foo.Foo7(0) == 7, "Calling IFoo.Foo7 on IBarImpl");
+        Test.Assert(foo.Foo8(0) == 8, "Calling IFoo.Foo8 on IBarImpl");
+        Test.Assert(foo.Foo9(0) == 9, "Calling IFoo.Foo9 on IBarImpl");
+
+        IBar bar = (IBar) barImpl;
+
+        Console.WriteLine("Calling IBar.Bar methods on IBarImpl...");
+
+        Test.Assert(bar.Bar1(0) == 11, "Calling IBar.Bar1 on IBarImpl");
+        Test.Assert(bar.Bar2(0) == 22, "Calling IBar.Bar2 on IBarImpl");
+        Test.Assert(bar.Bar3(0) == 33, "Calling IBar.Bar3 on IBarImpl");
+        Test.Assert(bar.Bar4(0) == 44, "Calling IBar.Bar4 on IBarImpl");
+        Test.Assert(bar.Bar5(0) == 55, "Calling IBar.Bar5 on IBarImpl");
+        Test.Assert(bar.Bar6(0) == 66, "Calling IBar.Bar6 on IBarImpl");
+        Test.Assert(bar.Bar7(0) == 77, "Calling IBar.Bar7 on IBarImpl");
+        Test.Assert(bar.Bar8(0) == 88, "Calling IBar.Bar8 on IBarImpl");
+        Test.Assert(bar.Bar9(0) == 99, "Calling IBar.Bar9 on IBarImpl");
+
+        IBlahImpl blahImpl = new IBlahImpl();
+        foo = (IFoo) blahImpl;
+
+        Test.Assert(foo.Foo1(1) == 11, "Calling IFoo.Foo1 on IBlahImpl");
+        Test.Assert(foo.Foo2(2) == 22, "Calling IFoo.Foo2 on IBlahImpl");
+        Test.Assert(foo.Foo3(3) == 33, "Calling IFoo.Foo3 on IBlahImpl");
+        Test.Assert(foo.Foo4(4) == 44, "Calling IFoo.Foo4 on IBlahImpl");
+        Test.Assert(foo.Foo5(5) == 55, "Calling IFoo.Foo5 on IBlahImpl");
+        Test.Assert(foo.Foo6(6) == 66, "Calling IFoo.Foo6 on IBlahImpl");
+        Test.Assert(foo.Foo7(7) == 77, "Calling IFoo.Foo7 on IBlahImpl");
+        Test.Assert(foo.Foo8(8) == 88, "Calling IFoo.Foo8 on IBlahImpl");
+        Test.Assert(foo.Foo9(9) == 99, "Calling IFoo.Foo9 on IBlahImpl");
+
+        bar = (IBar) blahImpl;
+
+        Console.WriteLine("Calling IBar.Bar methods on IBlahImpl...");
+
+        Test.Assert(bar.Bar1(1) == 111, "Calling IBar.Bar1 on IBlahImpl");
+        Test.Assert(bar.Bar2(2) == 222, "Calling IBar.Bar2 on IBlahImpl");
+        Test.Assert(bar.Bar3(3) == 333, "Calling IBar.Bar3 on IBlahImpl");
+        Test.Assert(bar.Bar4(4) == 444, "Calling IBar.Bar4 on IBlahImpl");
+        Test.Assert(bar.Bar5(5) == 555, "Calling IBar.Bar5 on IBlahImpl");
+        Test.Assert(bar.Bar6(0) == 66, "Calling IBar.Bar6 on IBlahImpl");
+        Test.Assert(bar.Bar7(0) == 77, "Calling IBar.Bar7 on IBlahImpl");
+        Test.Assert(bar.Bar8(0) == 88, "Calling IBar.Bar8 on IBlahImpl");
+        Test.Assert(bar.Bar9(0) == 99, "Calling IBar.Bar9 on IBlahImpl");  
+
+        IBlah blah = (IBlah) blahImpl;
+
+        Console.WriteLine("Calling IBlah.Blah methods on IBlahImpl...");   
+
+        Test.Assert(blah.Blah1(0) == 111, "Calling IBlah.Blah1 on IBlahImpl");
+        Test.Assert(blah.Blah2(2) == 2222, "Calling IBlah.Blah1 on IBlahImpl");
+        Test.Assert(blah.Blah3(3) == 3333, "Calling IBlah.Blah1 on IBlahImpl"); 
+    }
+
+    private static void MultiOverride()
+    {        
+        FooBarBlahImpl fooBarBlah = new FooBarBlahImpl();
+        IFoo foo = (IFoo) fooBarBlah;
+
+        Console.WriteLine("Calling IFoo.Foo methods on FooBarBlahImpl...");   
+        Test.Assert(foo.Foo1(0) == 11111, "Calling IFoo.Foo1 on FooBarBlahImpl");
+        Test.Assert(foo.Foo2(0) == 22222, "Calling IFoo.Foo2 on FooBarBlahImpl");
+        Test.Assert(foo.Foo3(0) == 33333, "Calling IFoo.Foo3 on FooBarBlahImpl");
+        Test.Assert(foo.Foo4(0) == 33333, "Calling IFoo.Foo4 on FooBarBlahImpl");
+        Test.Assert(foo.Foo5(0) == 33333, "Calling IFoo.Foo5 on FooBarBlahImpl");
+        Test.Assert(foo.Foo6(6) == 66, "Calling IFoo.Foo6 on FooBarBlahImpl");
+        Test.Assert(foo.Foo7(7) == 77, "Calling IFoo.Foo7 on FooBarBlahImpl");
+        Test.Assert(foo.Foo8(8) == 88, "Calling IFoo.Foo8 on FooBarBlahImpl");
+        Test.Assert(foo.Foo9(9) == 99, "Calling IFoo.Foo9 on FooBarBlahImpl"); 
+
+        IBar bar = (IBar) fooBarBlah;
+
+        Console.WriteLine("Calling IBar.Bar methods on FooBarBlahImpl...");
+
+        Test.Assert(bar.Bar1(0) == 11111, "Calling IBar.Bar1 on FooBarBlahImpl");
+        Test.Assert(bar.Bar2(0) == 22222, "Calling IBar.Bar2 on FooBarBlahImpl");
+        Test.Assert(bar.Bar3(0) == 33333, "Calling IBar.Bar3 on FooBarBlahImpl");
+        Test.Assert(bar.Bar4(0) == 33333, "Calling IBar.Bar4 on FooBarBlahImpl");
+        Test.Assert(bar.Bar5(0) == 33333, "Calling IBar.Bar5 on FooBarBlahImpl");
+        Test.Assert(bar.Bar6(0) == 66, "Calling IBar.Bar6 on FooBarBlahImpl");
+        Test.Assert(bar.Bar7(0) == 77, "Calling IBar.Bar7 on FooBarBlahImpl");
+        Test.Assert(bar.Bar8(0) == 88, "Calling IBar.Bar8 on FooBarBlahImpl");
+        Test.Assert(bar.Bar9(0) == 99, "Calling IBar.Bar9 on FooBarBlahImpl");            
+
+        IBlah blah = (IBlah) fooBarBlah;
+       
+        Console.WriteLine("Calling IBlah.Blah methods on FooBarBlahImpl...");   
+
+        Test.Assert(blah.Blah1(0) == 11111, "Calling IBlah.Blah1 on FooBarBlahImpl");
+        Test.Assert(blah.Blah2(0) == 22222, "Calling IBlah.Blah1 on FooBarBlahImpl");
+        Test.Assert(blah.Blah3(0) == 33333, "Calling IBlah.Blah1 on FooBarBlahImpl"); 
     }
 }
 
index 6ab45f0..7f94442 100644 (file)
 .class interface private abstract auto ansi IFoo
 {
   .method public hidebysig newslot virtual 
-          instance int32  Foo(int32 a) cil managed
+          instance int32  Foo1(int32 a) cil managed
   {
     // Code size       21 (0x15)
     .maxstack  2
     .locals init (int32 V_0)
     IL_0000:  nop
-    IL_0001:  ldstr      "At IFoo::Foo"
+    IL_0001:  ldstr      "At IIFoo.Foo1"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   1
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret    
+  } // end of method IFoo::Foo1
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo2(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo2"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   2
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret  
+  } // end of method IFoo::Foo2
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo3(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo3"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   3
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFoo::Foo3
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo4(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo4"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   4
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFoo::Foo4
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo5(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo5"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   5
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFoo::Foo5
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo6(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo6"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   6
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFoo::Foo6
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo7(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo7"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   7
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFoo::Foo7
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo8(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo8"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   8
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFoo::Foo8
+
+  .method public hidebysig newslot virtual 
+          instance int32  Foo9(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFoo.Foo9"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   9
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFoo::Foo9
+} // end of class IFoo
+
+.class interface private abstract auto ansi IBar
+       implements IFoo  
+{
+ .method public hidebysig newslot virtual 
+          instance int32  Bar1(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IIBar.Bar1"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   11
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret    
+  } // end of method IBar::Bar1
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar2(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar2"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   22
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret  
+  } // end of method IBar::Bar2
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar3(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar3"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   33
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBar::Bar3
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar4(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar4"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   44
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBar::Bar4
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar5(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar5"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   55
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBar::Bar5
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar6(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar6"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   66
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBar::Bar6
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar7(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar7"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   77
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBar::Bar7
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar8(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar8"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   88
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBar::Bar8
+
+  .method public hidebysig newslot virtual 
+          instance int32  Bar9(int32 a) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar.Bar9"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   99
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBar::Bar9 
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Overriding IFoo
+  //////////////////////////////////////////////////////////////////////////////
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo1(int32 a) cil managed
+  {
+    .override IFoo::Foo1
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar::IFoo.Foo1"
     IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
     IL_000b:  nop
     IL_000c:  ldarg.1
 
     IL_0013:  ldloc.0
     IL_0014:  ret
-  } // end of method IFoo::Foo
+  } // end of method IBar::IFoo.Foo1  
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo2(int32 a) cil managed
+  {
+    .override IFoo::Foo2
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar::IFoo.Foo2"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   20
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBar::IFoo.Foo2  
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo3(int32 a) cil managed
+  {
+    .override IFoo::Foo3
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar::IFoo.Foo3"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   30
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBar::IFoo.Foo3 
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo4(int32 a) cil managed
+  {
+    .override IFoo::Foo4
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar::IFoo.Foo4"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   40
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBar::IFoo.Foo4  
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo5(int32 a) cil managed
+  {
+    .override IFoo::Foo5
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBar::IFoo.Foo5"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   50
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBar::IFoo.Foo5 
+} // end of class IBar
+
+.class interface private abstract auto ansi IBlah
+       implements IBar,
+                  IFoo  
+{
+ .method public hidebysig newslot virtual
+          instance int32  Blah1(int32 c) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah.Blah1"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     111
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret     
+  } // end of method IBlah::Blah1
+
+  .method public hidebysig newslot virtual
+          instance int32  Blah2(int32 c) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah.Blah2"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     222
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::Blah2
+
+  .method public hidebysig newslot virtual
+          instance int32  Blah3(int32 c) cil managed
+  {
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah.Blah3"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     333
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBlah::Blah3   
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Overriding IFoo
+  //////////////////////////////////////////////////////////////////////////////
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo6(int32 a) cil managed
+  {
+    .override IFoo::Foo6
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IFoo.Foo6"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   60
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IFoo.Foo6   
+
+ .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo7(int32 a) cil managed
+  {
+    .override IFoo::Foo7
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IFoo.Foo7"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   70
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IFoo.Foo6  
+
+ .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo8(int32 a) cil managed
+  {
+    .override IFoo::Foo8
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IFoo.Foo8"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   80
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IFoo.Foo8  
 
-} // end of class IFoo
+ .method private hidebysig newslot virtual final 
+          instance int32  IFoo.Foo9(int32 a) cil managed
+  {
+    .override IFoo::Foo9
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IFoo.Foo9"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   90
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
 
-.class interface private abstract auto ansi IBar
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IFoo.Foo9
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Overriding IBar
+  //////////////////////////////////////////////////////////////////////////////
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IBar.Bar1(int32 a) cil managed
+  {
+    .override IBar::Bar1
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IBar.Bar1"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4.s   110
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IBar.Bar1   
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IBar.Bar2(int32 a) cil managed
+  {
+    .override IBar::Bar2
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IBar.Bar2"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     220
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IBar.Bar2
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IBar.Bar3(int32 a) cil managed
+  {
+    .override IBar::Bar3
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IBar.Bar3"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     330
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IBar.Bar3
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IBar.Bar4(int32 a) cil managed
+  {
+    .override IBar::Bar4
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IBar.Bar4"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     440
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IBar.Bar4
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IBar.Bar5(int32 a) cil managed
+  {
+    .override IBar::Bar5
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlah::IBar.Bar5"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     550
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret
+  } // end of method IBlah::IBar.Bar5  
+} // end of class IBlah
+
+.class private auto ansi beforefieldinit IBarImpl
+       extends [mscorlib]System.Object
+       implements IBar,
+                  IFoo  
 {
-  .method public hidebysig newslot virtual 
-          instance int32  Bar(int32 b) cil managed
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method IBarImpl::.ctor
+
+} // end of class IBarImpl
+
+.class private auto ansi beforefieldinit IBlahImpl
+       extends IBarImpl
+       implements IBlah,
+                  IBar,
+                  IFoo
+{
+  .method private hidebysig newslot virtual final 
+          instance int32  IBlah.Blah2(int32 c) cil managed
   {
+    .override IBlah::Blah2
     // Code size       21 (0x15)
     .maxstack  2
     .locals init (int32 V_0)
     IL_0000:  nop
-    IL_0001:  ldstr      "At IBar::Bar"
+    IL_0001:  ldstr      "At IBlahImpl::IBlah.Blah2"
     IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
     IL_000b:  nop
     IL_000c:  ldarg.1
-    IL_000d:  ldc.i4.s   20
+    IL_000d:  ldc.i4     2220
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IBlahImpl::IBlah.Blah2
+
+  .method private hidebysig newslot virtual final 
+          instance int32  IBlah.Blah3(int32 c) cil managed
+  {
+    .override IBlah::Blah3
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IBlahImpl::IBlah.Blah2"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     3330
     IL_000f:  add
     IL_0010:  stloc.0
     IL_0011:  br.s       IL_0013
 
     IL_0013:  ldloc.0
     IL_0014:  ret
-  } // end of method IBar::Bar
+  } // end of method IBlahImpl::IBlah.Blah3
 
-} // end of class IBar
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       8 (0x8)
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  call       instance void IBarImpl::.ctor()
+    IL_0006:  nop
+    IL_0007:  ret
+  } // end of method IBlahImpl::.ctor
 
-.class interface private abstract auto ansi IFooBar
+} // end of class IBlahImpl   
+.class interface private abstract auto ansi IFooBarBlah
        implements IFoo,
-                  IBar
+                  IBar,
+                  IBlah
 {
   .method private hidebysig newslot virtual final 
-          instance int32  IFoo.Foo(int32 a) cil managed
+          instance int32  IFooBarBlah.FooBarBlah1(int32 c) cil managed
   {
-    .override IFoo::Foo
+    .override IFoo::Foo1
+    .override IBar::Bar1
+    .override IBlah::Blah1
     // Code size       21 (0x15)
     .maxstack  2
     .locals init (int32 V_0)
     IL_0000:  nop
-    IL_0001:  ldstr      "At IFooBar::IFoo.Foo explicit methodimpl"
+    IL_0001:  ldstr      "At IFooBarBlah.FooBarBlah1"
     IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
     IL_000b:  nop
     IL_000c:  ldarg.1
-    IL_000d:  ldc.i4.s   30
+    IL_000d:  ldc.i4     11111
     IL_000f:  add
     IL_0010:  stloc.0
     IL_0011:  br.s       IL_0013
 
     IL_0013:  ldloc.0
-    IL_0014:  ret
-  } // end of method Temp::IFoo.Foo
+    IL_0014:  ret   
+  } // end of method IFooBarBlah.FooBarBlah1  
+  .method private hidebysig newslot virtual final 
+          instance int32  IFooBarBlah.FooBarBlah2(int32 c) cil managed
+  {
+    .override IFoo::Foo2
+    .override IBar::Bar2
+    .override IBlah::Blah2
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFooBarBlah.FooBarBlah2"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     22222
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
 
-} // end of class IFooBar
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFooBarBlah.FooBarBlah1
 
-.class private auto ansi beforefieldinit FooBar
-       extends [mscorlib]System.Object
-       implements IFooBar,
+  .method private hidebysig newslot virtual final 
+          instance int32  IFooBarBlah.FooBarBlah3(int32 c) cil managed
+  {
+    .override IFoo::Foo3
+    .override IFoo::Foo4
+    .override IFoo::Foo5
+    .override IBar::Bar3
+    .override IBar::Bar4
+    .override IBar::Bar5
+    .override IBlah::Blah3
+    // Code size       21 (0x15)
+    .maxstack  2
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  ldstr      "At IFooBarBlah.FooBarBlah3"
+    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_000b:  nop
+    IL_000c:  ldarg.1
+    IL_000d:  ldc.i4     33333
+    IL_000f:  add
+    IL_0010:  stloc.0
+    IL_0011:  br.s       IL_0013
+
+    IL_0013:  ldloc.0
+    IL_0014:  ret   
+  } // end of method IFooBarBlah.FooBarBlah3
+} // end of class IFooBarBlah  
+
+.class private auto ansi beforefieldinit FooBarBlahImpl
+       implements IFooBarBlah,
                   IFoo,
-                  IBar
+                  IBar,
+                  IBlah
 {
   .method public hidebysig specialname rtspecialname 
           instance void  .ctor() cil managed
     // Code size       8 (0x8)
     .maxstack  8
     IL_0000:  ldarg.0
-    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_0001:  call       instance void IBlahImpl::.ctor()
     IL_0006:  nop
     IL_0007:  ret
-  } // end of method FooBar::.ctor
+  } // end of method FooBarBlahImpl::.ctor
 
-} // end of class FooBar
+} // end of class FooBarBlahImpl         
 
 .class private auto ansi beforefieldinit Program
        extends [mscorlib]System.Object
   .method public hidebysig static int32  Main() cil managed
   {
     .entrypoint
-    // Code size       89 (0x59)
+    // Code size       23 (0x17)
+    .maxstack  1
+    .locals init (int32 V_0)
+    IL_0000:  nop
+    IL_0001:  call       void Program::SingleOverride()
+    IL_0006:  nop
+    IL_0007:  call       void Program::MultiOverride()
+    IL_000c:  nop
+    IL_000d:  call       int32 Test::Ret()
+    IL_0012:  stloc.0
+    IL_0013:  br.s       IL_0015
+
+    IL_0015:  ldloc.0
+    IL_0016:  ret
+  } // end of method Program::Main
+
+  .method private hidebysig static void  SingleOverride() cil managed
+  {
+    // Code size       946 (0x3b2)
     .maxstack  2
-    .locals init (class FooBar V_0,
+    .locals init (class IBarImpl V_0,
              class IFoo V_1,
              class IBar V_2,
-             int32 V_3)
+             class IBlahImpl V_3,
+             class IBlah V_4)
     IL_0000:  nop
-    IL_0001:  newobj     instance void FooBar::.ctor()
+    IL_0001:  newobj     instance void IBarImpl::.ctor()
     IL_0006:  stloc.0
     IL_0007:  ldloc.0
     IL_0008:  stloc.1
-    IL_0009:  ldloc.0
-    IL_000a:  stloc.2
-    IL_000b:  ldstr      "Calling IFoo.Foo on FooBar - expecting IFooBar::IF"
-    + "oo.Bar"
-    IL_0010:  call       void [mscorlib]System.Console::WriteLine(string)
-    IL_0015:  nop
-    IL_0016:  ldloc.1
-    IL_0017:  ldc.i4.s   10
-    IL_0019:  callvirt   instance int32 IFoo::Foo(int32)
-    IL_001e:  ldc.i4.s   40
+    IL_0009:  ldstr      "Calling IFoo.Foo methods on IBarImpl..."
+    IL_000e:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_0013:  nop
+    IL_0014:  ldloc.1
+    IL_0015:  ldc.i4.1
+    IL_0016:  callvirt   instance int32 IFoo::Foo1(int32)
+    IL_001b:  ldc.i4.s   11
+    IL_001d:  ceq
+    IL_001f:  ldstr      "Calling IFoo.Foo1 on IBarImpl"
+    IL_0024:  call       void Test::Assert(bool,
+                                           string)
+    IL_0029:  nop
+    IL_002a:  ldloc.1
+    IL_002b:  ldc.i4.2
+    IL_002c:  callvirt   instance int32 IFoo::Foo2(int32)
+    IL_0031:  ldc.i4.s   22
+    IL_0033:  ceq
+    IL_0035:  ldstr      "Calling IFoo.Foo2 on IBarImpl"
+    IL_003a:  call       void Test::Assert(bool,
+                                           string)
+    IL_003f:  nop
+    IL_0040:  ldloc.1
+    IL_0041:  ldc.i4.3
+    IL_0042:  callvirt   instance int32 IFoo::Foo3(int32)
+    IL_0047:  ldc.i4.s   33
+    IL_0049:  ceq
+    IL_004b:  ldstr      "Calling IFoo.Foo3 on IBarImpl"
+    IL_0050:  call       void Test::Assert(bool,
+                                           string)
+    IL_0055:  nop
+    IL_0056:  ldloc.1
+    IL_0057:  ldc.i4.4
+    IL_0058:  callvirt   instance int32 IFoo::Foo4(int32)
+    IL_005d:  ldc.i4.s   44
+    IL_005f:  ceq
+    IL_0061:  ldstr      "Calling IFoo.Foo4 on IBarImpl"
+    IL_0066:  call       void Test::Assert(bool,
+                                           string)
+    IL_006b:  nop
+    IL_006c:  ldloc.1
+    IL_006d:  ldc.i4.5
+    IL_006e:  callvirt   instance int32 IFoo::Foo5(int32)
+    IL_0073:  ldc.i4.s   55
+    IL_0075:  ceq
+    IL_0077:  ldstr      "Calling IFoo.Foo5 on IBarImpl"
+    IL_007c:  call       void Test::Assert(bool,
+                                           string)
+    IL_0081:  nop
+    IL_0082:  ldloc.1
+    IL_0083:  ldc.i4.0
+    IL_0084:  callvirt   instance int32 IFoo::Foo6(int32)
+    IL_0089:  ldc.i4.6
+    IL_008a:  ceq
+    IL_008c:  ldstr      "Calling IFoo.Foo6 on IBarImpl"
+    IL_0091:  call       void Test::Assert(bool,
+                                           string)
+    IL_0096:  nop
+    IL_0097:  ldloc.1
+    IL_0098:  ldc.i4.0
+    IL_0099:  callvirt   instance int32 IFoo::Foo7(int32)
+    IL_009e:  ldc.i4.7
+    IL_009f:  ceq
+    IL_00a1:  ldstr      "Calling IFoo.Foo7 on IBarImpl"
+    IL_00a6:  call       void Test::Assert(bool,
+                                           string)
+    IL_00ab:  nop
+    IL_00ac:  ldloc.1
+    IL_00ad:  ldc.i4.0
+    IL_00ae:  callvirt   instance int32 IFoo::Foo8(int32)
+    IL_00b3:  ldc.i4.8
+    IL_00b4:  ceq
+    IL_00b6:  ldstr      "Calling IFoo.Foo8 on IBarImpl"
+    IL_00bb:  call       void Test::Assert(bool,
+                                           string)
+    IL_00c0:  nop
+    IL_00c1:  ldloc.1
+    IL_00c2:  ldc.i4.0
+    IL_00c3:  callvirt   instance int32 IFoo::Foo9(int32)
+    IL_00c8:  ldc.i4.s   9
+    IL_00ca:  ceq
+    IL_00cc:  ldstr      "Calling IFoo.Foo9 on IBarImpl"
+    IL_00d1:  call       void Test::Assert(bool,
+                                           string)
+    IL_00d6:  nop
+    IL_00d7:  ldloc.0
+    IL_00d8:  stloc.2
+    IL_00d9:  ldstr      "Calling IBar.Bar methods on IBarImpl..."
+    IL_00de:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_00e3:  nop
+    IL_00e4:  ldloc.2
+    IL_00e5:  ldc.i4.0
+    IL_00e6:  callvirt   instance int32 IBar::Bar1(int32)
+    IL_00eb:  ldc.i4.s   11
+    IL_00ed:  ceq
+    IL_00ef:  ldstr      "Calling IBar.Bar1 on IBarImpl"
+    IL_00f4:  call       void Test::Assert(bool,
+                                           string)
+    IL_00f9:  nop
+    IL_00fa:  ldloc.2
+    IL_00fb:  ldc.i4.0
+    IL_00fc:  callvirt   instance int32 IBar::Bar2(int32)
+    IL_0101:  ldc.i4.s   22
+    IL_0103:  ceq
+    IL_0105:  ldstr      "Calling IBar.Bar2 on IBarImpl"
+    IL_010a:  call       void Test::Assert(bool,
+                                           string)
+    IL_010f:  nop
+    IL_0110:  ldloc.2
+    IL_0111:  ldc.i4.0
+    IL_0112:  callvirt   instance int32 IBar::Bar3(int32)
+    IL_0117:  ldc.i4.s   33
+    IL_0119:  ceq
+    IL_011b:  ldstr      "Calling IBar.Bar3 on IBarImpl"
+    IL_0120:  call       void Test::Assert(bool,
+                                           string)
+    IL_0125:  nop
+    IL_0126:  ldloc.2
+    IL_0127:  ldc.i4.0
+    IL_0128:  callvirt   instance int32 IBar::Bar4(int32)
+    IL_012d:  ldc.i4.s   44
+    IL_012f:  ceq
+    IL_0131:  ldstr      "Calling IBar.Bar4 on IBarImpl"
+    IL_0136:  call       void Test::Assert(bool,
+                                           string)
+    IL_013b:  nop
+    IL_013c:  ldloc.2
+    IL_013d:  ldc.i4.0
+    IL_013e:  callvirt   instance int32 IBar::Bar5(int32)
+    IL_0143:  ldc.i4.s   55
+    IL_0145:  ceq
+    IL_0147:  ldstr      "Calling IBar.Bar5 on IBarImpl"
+    IL_014c:  call       void Test::Assert(bool,
+                                           string)
+    IL_0151:  nop
+    IL_0152:  ldloc.2
+    IL_0153:  ldc.i4.0
+    IL_0154:  callvirt   instance int32 IBar::Bar6(int32)
+    IL_0159:  ldc.i4.s   66
+    IL_015b:  ceq
+    IL_015d:  ldstr      "Calling IBar.Bar6 on IBarImpl"
+    IL_0162:  call       void Test::Assert(bool,
+                                           string)
+    IL_0167:  nop
+    IL_0168:  ldloc.2
+    IL_0169:  ldc.i4.0
+    IL_016a:  callvirt   instance int32 IBar::Bar7(int32)
+    IL_016f:  ldc.i4.s   77
+    IL_0171:  ceq
+    IL_0173:  ldstr      "Calling IBar.Bar7 on IBarImpl"
+    IL_0178:  call       void Test::Assert(bool,
+                                           string)
+    IL_017d:  nop
+    IL_017e:  ldloc.2
+    IL_017f:  ldc.i4.0
+    IL_0180:  callvirt   instance int32 IBar::Bar8(int32)
+    IL_0185:  ldc.i4.s   88
+    IL_0187:  ceq
+    IL_0189:  ldstr      "Calling IBar.Bar8 on IBarImpl"
+    IL_018e:  call       void Test::Assert(bool,
+                                           string)
+    IL_0193:  nop
+    IL_0194:  ldloc.2
+    IL_0195:  ldc.i4.0
+    IL_0196:  callvirt   instance int32 IBar::Bar9(int32)
+    IL_019b:  ldc.i4.s   99
+    IL_019d:  ceq
+    IL_019f:  ldstr      "Calling IBar.Bar9 on IBarImpl"
+    IL_01a4:  call       void Test::Assert(bool,
+                                           string)
+    IL_01a9:  nop
+    IL_01aa:  newobj     instance void IBlahImpl::.ctor()
+    IL_01af:  stloc.3
+    IL_01b0:  ldloc.3
+    IL_01b1:  stloc.1
+    IL_01b2:  ldloc.1
+    IL_01b3:  ldc.i4.1
+    IL_01b4:  callvirt   instance int32 IFoo::Foo1(int32)
+    IL_01b9:  ldc.i4.s   11
+    IL_01bb:  ceq
+    IL_01bd:  ldstr      "Calling IFoo.Foo1 on IBlahImpl"
+    IL_01c2:  call       void Test::Assert(bool,
+                                           string)
+    IL_01c7:  nop
+    IL_01c8:  ldloc.1
+    IL_01c9:  ldc.i4.2
+    IL_01ca:  callvirt   instance int32 IFoo::Foo2(int32)
+    IL_01cf:  ldc.i4.s   22
+    IL_01d1:  ceq
+    IL_01d3:  ldstr      "Calling IFoo.Foo2 on IBlahImpl"
+    IL_01d8:  call       void Test::Assert(bool,
+                                           string)
+    IL_01dd:  nop
+    IL_01de:  ldloc.1
+    IL_01df:  ldc.i4.3
+    IL_01e0:  callvirt   instance int32 IFoo::Foo3(int32)
+    IL_01e5:  ldc.i4.s   33
+    IL_01e7:  ceq
+    IL_01e9:  ldstr      "Calling IFoo.Foo3 on IBlahImpl"
+    IL_01ee:  call       void Test::Assert(bool,
+                                           string)
+    IL_01f3:  nop
+    IL_01f4:  ldloc.1
+    IL_01f5:  ldc.i4.4
+    IL_01f6:  callvirt   instance int32 IFoo::Foo4(int32)
+    IL_01fb:  ldc.i4.s   44
+    IL_01fd:  ceq
+    IL_01ff:  ldstr      "Calling IFoo.Foo4 on IBlahImpl"
+    IL_0204:  call       void Test::Assert(bool,
+                                           string)
+    IL_0209:  nop
+    IL_020a:  ldloc.1
+    IL_020b:  ldc.i4.5
+    IL_020c:  callvirt   instance int32 IFoo::Foo5(int32)
+    IL_0211:  ldc.i4.s   55
+    IL_0213:  ceq
+    IL_0215:  ldstr      "Calling IFoo.Foo5 on IBlahImpl"
+    IL_021a:  call       void Test::Assert(bool,
+                                           string)
+    IL_021f:  nop
+    IL_0220:  ldloc.1
+    IL_0221:  ldc.i4.6
+    IL_0222:  callvirt   instance int32 IFoo::Foo6(int32)
+    IL_0227:  ldc.i4.s   66
+    IL_0229:  ceq
+    IL_022b:  ldstr      "Calling IFoo.Foo6 on IBlahImpl"
+    IL_0230:  call       void Test::Assert(bool,
+                                           string)
+    IL_0235:  nop
+    IL_0236:  ldloc.1
+    IL_0237:  ldc.i4.7
+    IL_0238:  callvirt   instance int32 IFoo::Foo7(int32)
+    IL_023d:  ldc.i4.s   77
+    IL_023f:  ceq
+    IL_0241:  ldstr      "Calling IFoo.Foo7 on IBlahImpl"
+    IL_0246:  call       void Test::Assert(bool,
+                                           string)
+    IL_024b:  nop
+    IL_024c:  ldloc.1
+    IL_024d:  ldc.i4.8
+    IL_024e:  callvirt   instance int32 IFoo::Foo8(int32)
+    IL_0253:  ldc.i4.s   88
+    IL_0255:  ceq
+    IL_0257:  ldstr      "Calling IFoo.Foo8 on IBlahImpl"
+    IL_025c:  call       void Test::Assert(bool,
+                                           string)
+    IL_0261:  nop
+    IL_0262:  ldloc.1
+    IL_0263:  ldc.i4.s   9
+    IL_0265:  callvirt   instance int32 IFoo::Foo9(int32)
+    IL_026a:  ldc.i4.s   99
+    IL_026c:  ceq
+    IL_026e:  ldstr      "Calling IFoo.Foo9 on IBlahImpl"
+    IL_0273:  call       void Test::Assert(bool,
+                                           string)
+    IL_0278:  nop
+    IL_0279:  ldloc.3
+    IL_027a:  stloc.2
+    IL_027b:  ldstr      "Calling IBar.Bar methods on IBlahImpl..."
+    IL_0280:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_0285:  nop
+    IL_0286:  ldloc.2
+    IL_0287:  ldc.i4.1
+    IL_0288:  callvirt   instance int32 IBar::Bar1(int32)
+    IL_028d:  ldc.i4.s   111
+    IL_028f:  ceq
+    IL_0291:  ldstr      "Calling IBar.Bar1 on IBlahImpl"
+    IL_0296:  call       void Test::Assert(bool,
+                                           string)
+    IL_029b:  nop
+    IL_029c:  ldloc.2
+    IL_029d:  ldc.i4.2
+    IL_029e:  callvirt   instance int32 IBar::Bar2(int32)
+    IL_02a3:  ldc.i4     0xde
+    IL_02a8:  ceq
+    IL_02aa:  ldstr      "Calling IBar.Bar2 on IBlahImpl"
+    IL_02af:  call       void Test::Assert(bool,
+                                           string)
+    IL_02b4:  nop
+    IL_02b5:  ldloc.2
+    IL_02b6:  ldc.i4.3
+    IL_02b7:  callvirt   instance int32 IBar::Bar3(int32)
+    IL_02bc:  ldc.i4     0x14d
+    IL_02c1:  ceq
+    IL_02c3:  ldstr      "Calling IBar.Bar3 on IBlahImpl"
+    IL_02c8:  call       void Test::Assert(bool,
+                                           string)
+    IL_02cd:  nop
+    IL_02ce:  ldloc.2
+    IL_02cf:  ldc.i4.4
+    IL_02d0:  callvirt   instance int32 IBar::Bar4(int32)
+    IL_02d5:  ldc.i4     0x1bc
+    IL_02da:  ceq
+    IL_02dc:  ldstr      "Calling IBar.Bar4 on IBlahImpl"
+    IL_02e1:  call       void Test::Assert(bool,
+                                           string)
+    IL_02e6:  nop
+    IL_02e7:  ldloc.2
+    IL_02e8:  ldc.i4.5
+    IL_02e9:  callvirt   instance int32 IBar::Bar5(int32)
+    IL_02ee:  ldc.i4     0x22b
+    IL_02f3:  ceq
+    IL_02f5:  ldstr      "Calling IBar.Bar5 on IBlahImpl"
+    IL_02fa:  call       void Test::Assert(bool,
+                                           string)
+    IL_02ff:  nop
+    IL_0300:  ldloc.2
+    IL_0301:  ldc.i4.0
+    IL_0302:  callvirt   instance int32 IBar::Bar6(int32)
+    IL_0307:  ldc.i4.s   66
+    IL_0309:  ceq
+    IL_030b:  ldstr      "Calling IBar.Bar6 on IBlahImpl"
+    IL_0310:  call       void Test::Assert(bool,
+                                           string)
+    IL_0315:  nop
+    IL_0316:  ldloc.2
+    IL_0317:  ldc.i4.0
+    IL_0318:  callvirt   instance int32 IBar::Bar7(int32)
+    IL_031d:  ldc.i4.s   77
+    IL_031f:  ceq
+    IL_0321:  ldstr      "Calling IBar.Bar7 on IBlahImpl"
+    IL_0326:  call       void Test::Assert(bool,
+                                           string)
+    IL_032b:  nop
+    IL_032c:  ldloc.2
+    IL_032d:  ldc.i4.0
+    IL_032e:  callvirt   instance int32 IBar::Bar8(int32)
+    IL_0333:  ldc.i4.s   88
+    IL_0335:  ceq
+    IL_0337:  ldstr      "Calling IBar.Bar8 on IBlahImpl"
+    IL_033c:  call       void Test::Assert(bool,
+                                           string)
+    IL_0341:  nop
+    IL_0342:  ldloc.2
+    IL_0343:  ldc.i4.0
+    IL_0344:  callvirt   instance int32 IBar::Bar9(int32)
+    IL_0349:  ldc.i4.s   99
+    IL_034b:  ceq
+    IL_034d:  ldstr      "Calling IBar.Bar9 on IBlahImpl"
+    IL_0352:  call       void Test::Assert(bool,
+                                           string)
+    IL_0357:  nop
+    IL_0358:  ldloc.3
+    IL_0359:  stloc.s    V_4
+    IL_035b:  ldstr      "Calling IBlah.Blah methods on IBlahImpl..."
+    IL_0360:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_0365:  nop
+    IL_0366:  ldloc.s    V_4
+    IL_0368:  ldc.i4.0
+    IL_0369:  callvirt   instance int32 IBlah::Blah1(int32)
+    IL_036e:  ldc.i4.s   111
+    IL_0370:  ceq
+    IL_0372:  ldstr      "Calling IBlah.Blah1 on IBlahImpl"
+    IL_0377:  call       void Test::Assert(bool,
+                                           string)
+    IL_037c:  nop
+    IL_037d:  ldloc.s    V_4
+    IL_037f:  ldc.i4.2
+    IL_0380:  callvirt   instance int32 IBlah::Blah2(int32)
+    IL_0385:  ldc.i4     0x8ae
+    IL_038a:  ceq
+    IL_038c:  ldstr      "Calling IBlah.Blah1 on IBlahImpl"
+    IL_0391:  call       void Test::Assert(bool,
+                                           string)
+    IL_0396:  nop
+    IL_0397:  ldloc.s    V_4
+    IL_0399:  ldc.i4.3
+    IL_039a:  callvirt   instance int32 IBlah::Blah3(int32)
+    IL_039f:  ldc.i4     0xd05
+    IL_03a4:  ceq
+    IL_03a6:  ldstr      "Calling IBlah.Blah1 on IBlahImpl"
+    IL_03ab:  call       void Test::Assert(bool,
+                                           string)
+    IL_03b0:  nop
+    IL_03b1:  ret
+  } // end of method Program::SingleOverride
+
+  .method private hidebysig static void  MultiOverride() cil managed
+  {
+    // Code size       549 (0x225)
+    .maxstack  2
+    .locals init (class FooBarBlahImpl V_0,
+             class IFoo V_1,
+             class IBar V_2,
+             class IBlah V_3)
+    IL_0000:  nop
+    IL_0001:  newobj     instance void FooBarBlahImpl::.ctor()
+    IL_0006:  stloc.0
+    IL_0007:  ldloc.0
+    IL_0008:  stloc.1
+    IL_0009:  ldstr      "Calling IFoo.Foo methods on FooBarBlahImpl..."
+    IL_000e:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_0013:  nop
+    IL_0014:  ldloc.1
+    IL_0015:  ldc.i4.0
+    IL_0016:  callvirt   instance int32 IFoo::Foo1(int32)
+    IL_001b:  ldc.i4     0x2b67
     IL_0020:  ceq
-    IL_0022:  ldstr      "Calling IFoo.Foo on FooBar"
+    IL_0022:  ldstr      "Calling IFoo.Foo1 on FooBarBlahImpl"
     IL_0027:  call       void Test::Assert(bool,
                                            string)
     IL_002c:  nop
-    IL_002d:  ldstr      "Calling IBar.Bar on FooBar - expecting IBar::Bar"
-    IL_0032:  call       void [mscorlib]System.Console::WriteLine(string)
-    IL_0037:  nop
-    IL_0038:  ldloc.2
-    IL_0039:  ldc.i4.s   10
-    IL_003b:  callvirt   instance int32 IBar::Bar(int32)
-    IL_0040:  ldc.i4.s   30
-    IL_0042:  ceq
-    IL_0044:  ldstr      "Calling IBar.Bar on FooBar"
-    IL_0049:  call       void Test::Assert(bool,
-                                           string)
-    IL_004e:  nop
-    IL_004f:  call       int32 Test::Ret()
-    IL_0054:  stloc.3
-    IL_0055:  br.s       IL_0057
-
-    IL_0057:  ldloc.3
-    IL_0058:  ret
-  } // end of method Program::Main
+    IL_002d:  ldloc.1
+    IL_002e:  ldc.i4.0
+    IL_002f:  callvirt   instance int32 IFoo::Foo2(int32)
+    IL_0034:  ldc.i4     0x56ce
+    IL_0039:  ceq
+    IL_003b:  ldstr      "Calling IFoo.Foo2 on FooBarBlahImpl"
+    IL_0040:  call       void Test::Assert(bool,
+                                           string)
+    IL_0045:  nop
+    IL_0046:  ldloc.1
+    IL_0047:  ldc.i4.0
+    IL_0048:  callvirt   instance int32 IFoo::Foo3(int32)
+    IL_004d:  ldc.i4     0x8235
+    IL_0052:  ceq
+    IL_0054:  ldstr      "Calling IFoo.Foo3 on FooBarBlahImpl"
+    IL_0059:  call       void Test::Assert(bool,
+                                           string)
+    IL_005e:  nop
+    IL_005f:  ldloc.1
+    IL_0060:  ldc.i4.0
+    IL_0061:  callvirt   instance int32 IFoo::Foo4(int32)
+    IL_0066:  ldc.i4     0x8235
+    IL_006b:  ceq
+    IL_006d:  ldstr      "Calling IFoo.Foo4 on FooBarBlahImpl"
+    IL_0072:  call       void Test::Assert(bool,
+                                           string)
+    IL_0077:  nop
+    IL_0078:  ldloc.1
+    IL_0079:  ldc.i4.0
+    IL_007a:  callvirt   instance int32 IFoo::Foo5(int32)
+    IL_007f:  ldc.i4     0x8235
+    IL_0084:  ceq
+    IL_0086:  ldstr      "Calling IFoo.Foo5 on FooBarBlahImpl"
+    IL_008b:  call       void Test::Assert(bool,
+                                           string)
+    IL_0090:  nop
+    IL_0091:  ldloc.1
+    IL_0092:  ldc.i4.6
+    IL_0093:  callvirt   instance int32 IFoo::Foo6(int32)
+    IL_0098:  ldc.i4.s   66
+    IL_009a:  ceq
+    IL_009c:  ldstr      "Calling IFoo.Foo6 on FooBarBlahImpl"
+    IL_00a1:  call       void Test::Assert(bool,
+                                           string)
+    IL_00a6:  nop
+    IL_00a7:  ldloc.1
+    IL_00a8:  ldc.i4.7
+    IL_00a9:  callvirt   instance int32 IFoo::Foo7(int32)
+    IL_00ae:  ldc.i4.s   77
+    IL_00b0:  ceq
+    IL_00b2:  ldstr      "Calling IFoo.Foo7 on FooBarBlahImpl"
+    IL_00b7:  call       void Test::Assert(bool,
+                                           string)
+    IL_00bc:  nop
+    IL_00bd:  ldloc.1
+    IL_00be:  ldc.i4.8
+    IL_00bf:  callvirt   instance int32 IFoo::Foo8(int32)
+    IL_00c4:  ldc.i4.s   88
+    IL_00c6:  ceq
+    IL_00c8:  ldstr      "Calling IFoo.Foo8 on FooBarBlahImpl"
+    IL_00cd:  call       void Test::Assert(bool,
+                                           string)
+    IL_00d2:  nop
+    IL_00d3:  ldloc.1
+    IL_00d4:  ldc.i4.s   9
+    IL_00d6:  callvirt   instance int32 IFoo::Foo9(int32)
+    IL_00db:  ldc.i4.s   99
+    IL_00dd:  ceq
+    IL_00df:  ldstr      "Calling IFoo.Foo9 on FooBarBlahImpl"
+    IL_00e4:  call       void Test::Assert(bool,
+                                           string)
+    IL_00e9:  nop
+    IL_00ea:  ldloc.0
+    IL_00eb:  stloc.2
+    IL_00ec:  ldstr      "Calling IBar.Bar methods on FooBarBlahImpl..."
+    IL_00f1:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_00f6:  nop
+    IL_00f7:  ldloc.2
+    IL_00f8:  ldc.i4.0
+    IL_00f9:  callvirt   instance int32 IBar::Bar1(int32)
+    IL_00fe:  ldc.i4     0x2b67
+    IL_0103:  ceq
+    IL_0105:  ldstr      "Calling IBar.Bar1 on FooBarBlahImpl"
+    IL_010a:  call       void Test::Assert(bool,
+                                           string)
+    IL_010f:  nop
+    IL_0110:  ldloc.2
+    IL_0111:  ldc.i4.0
+    IL_0112:  callvirt   instance int32 IBar::Bar2(int32)
+    IL_0117:  ldc.i4     0x56ce
+    IL_011c:  ceq
+    IL_011e:  ldstr      "Calling IBar.Bar2 on FooBarBlahImpl"
+    IL_0123:  call       void Test::Assert(bool,
+                                           string)
+    IL_0128:  nop
+    IL_0129:  ldloc.2
+    IL_012a:  ldc.i4.0
+    IL_012b:  callvirt   instance int32 IBar::Bar3(int32)
+    IL_0130:  ldc.i4     0x8235
+    IL_0135:  ceq
+    IL_0137:  ldstr      "Calling IBar.Bar3 on FooBarBlahImpl"
+    IL_013c:  call       void Test::Assert(bool,
+                                           string)
+    IL_0141:  nop
+    IL_0142:  ldloc.2
+    IL_0143:  ldc.i4.0
+    IL_0144:  callvirt   instance int32 IBar::Bar4(int32)
+    IL_0149:  ldc.i4     0x8235
+    IL_014e:  ceq
+    IL_0150:  ldstr      "Calling IBar.Bar4 on FooBarBlahImpl"
+    IL_0155:  call       void Test::Assert(bool,
+                                           string)
+    IL_015a:  nop
+    IL_015b:  ldloc.2
+    IL_015c:  ldc.i4.0
+    IL_015d:  callvirt   instance int32 IBar::Bar5(int32)
+    IL_0162:  ldc.i4     0x8235
+    IL_0167:  ceq
+    IL_0169:  ldstr      "Calling IBar.Bar5 on FooBarBlahImpl"
+    IL_016e:  call       void Test::Assert(bool,
+                                           string)
+    IL_0173:  nop
+    IL_0174:  ldloc.2
+    IL_0175:  ldc.i4.0
+    IL_0176:  callvirt   instance int32 IBar::Bar6(int32)
+    IL_017b:  ldc.i4.s   66
+    IL_017d:  ceq
+    IL_017f:  ldstr      "Calling IBar.Bar6 on FooBarBlahImpl"
+    IL_0184:  call       void Test::Assert(bool,
+                                           string)
+    IL_0189:  nop
+    IL_018a:  ldloc.2
+    IL_018b:  ldc.i4.0
+    IL_018c:  callvirt   instance int32 IBar::Bar7(int32)
+    IL_0191:  ldc.i4.s   77
+    IL_0193:  ceq
+    IL_0195:  ldstr      "Calling IBar.Bar7 on FooBarBlahImpl"
+    IL_019a:  call       void Test::Assert(bool,
+                                           string)
+    IL_019f:  nop
+    IL_01a0:  ldloc.2
+    IL_01a1:  ldc.i4.0
+    IL_01a2:  callvirt   instance int32 IBar::Bar8(int32)
+    IL_01a7:  ldc.i4.s   88
+    IL_01a9:  ceq
+    IL_01ab:  ldstr      "Calling IBar.Bar8 on FooBarBlahImpl"
+    IL_01b0:  call       void Test::Assert(bool,
+                                           string)
+    IL_01b5:  nop
+    IL_01b6:  ldloc.2
+    IL_01b7:  ldc.i4.0
+    IL_01b8:  callvirt   instance int32 IBar::Bar9(int32)
+    IL_01bd:  ldc.i4.s   99
+    IL_01bf:  ceq
+    IL_01c1:  ldstr      "Calling IBar.Bar9 on FooBarBlahImpl"
+    IL_01c6:  call       void Test::Assert(bool,
+                                           string)
+    IL_01cb:  nop
+    IL_01cc:  ldloc.0
+    IL_01cd:  stloc.3
+    IL_01ce:  ldstr      "Calling IBlah.Blah methods on FooBarBlahImpl..."
+    IL_01d3:  call       void [mscorlib]System.Console::WriteLine(string)
+    IL_01d8:  nop
+    IL_01d9:  ldloc.3
+    IL_01da:  ldc.i4.0
+    IL_01db:  callvirt   instance int32 IBlah::Blah1(int32)
+    IL_01e0:  ldc.i4     0x2b67
+    IL_01e5:  ceq
+    IL_01e7:  ldstr      "Calling IBlah.Blah1 on FooBarBlahImpl"
+    IL_01ec:  call       void Test::Assert(bool,
+                                           string)
+    IL_01f1:  nop
+    IL_01f2:  ldloc.3
+    IL_01f3:  ldc.i4.0
+    IL_01f4:  callvirt   instance int32 IBlah::Blah2(int32)
+    IL_01f9:  ldc.i4     0x56ce
+    IL_01fe:  ceq
+    IL_0200:  ldstr      "Calling IBlah.Blah1 on FooBarBlahImpl"
+    IL_0205:  call       void Test::Assert(bool,
+                                           string)
+    IL_020a:  nop
+    IL_020b:  ldloc.3
+    IL_020c:  ldc.i4.0
+    IL_020d:  callvirt   instance int32 IBlah::Blah3(int32)
+    IL_0212:  ldc.i4     0x8235
+    IL_0217:  ceq
+    IL_0219:  ldstr      "Calling IBlah.Blah1 on FooBarBlahImpl"
+    IL_021e:  call       void Test::Assert(bool,
+                                           string)
+    IL_0223:  nop
+    IL_0224:  ret
+  } // end of method Program::MultiOverride
 
   .method public hidebysig specialname rtspecialname 
           instance void  .ctor() cil managed
     IL_0007:  ret
   } // end of method Program::.ctor
 
-} // end of class Program
+} // end of class Program  
 
 .class private auto ansi beforefieldinit Test
        extends [mscorlib]System.Object