Allow supressing exceptions in diamond inheritance cases (#20458)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Thu, 15 Nov 2018 13:08:27 +0000 (14:08 +0100)
committerGitHub <noreply@github.com>
Thu, 15 Nov 2018 13:08:27 +0000 (14:08 +0100)
12 files changed:
src/vm/comcallablewrapper.cpp
src/vm/comdelegate.cpp
src/vm/custommarshalerinfo.cpp
src/vm/dispatchinfo.cpp
src/vm/jitinterface.cpp
src/vm/methodtable.cpp
src/vm/methodtable.h
src/vm/runtimehandles.cpp
src/vm/virtualcallstub.cpp
src/vm/virtualcallstub.h
tests/src/Regressions/coreclr/16123/ambiguousconstraint.il [new file with mode: 0644]
tests/src/Regressions/coreclr/16123/ambiguousconstraint.ilproj [new file with mode: 0644]

index 3620824..06a99e1 100644 (file)
@@ -4678,7 +4678,7 @@ BOOL ComMethodTable::LayOutInterfaceMethodTable(MethodTable* pClsMT)
         }
         else if (pClsMT != NULL)
         {
-            DispatchSlot impl(pClsMT->FindDispatchSlotForInterfaceMD(pIntfMD));
+            DispatchSlot impl(pClsMT->FindDispatchSlotForInterfaceMD(pIntfMD, FALSE /* throwOnConflict */));
             pClassMD = impl.GetMethodDesc();
         }
 
@@ -6494,7 +6494,8 @@ MethodDesc * ComCallWrapperTemplate::GetICustomQueryInterfaceGetInterfaceMD()
 
     if (m_pICustomQueryInterfaceGetInterfaceMD == NULL)
         m_pICustomQueryInterfaceGetInterfaceMD = m_thClass.GetMethodTable()->GetMethodDescForInterfaceMethod(
-           MscorlibBinder::GetMethod(METHOD__ICUSTOM_QUERYINTERFACE__GET_INTERFACE));
+           MscorlibBinder::GetMethod(METHOD__ICUSTOM_QUERYINTERFACE__GET_INTERFACE),
+           TRUE /* throwOnConflict */);
     RETURN m_pICustomQueryInterfaceGetInterfaceMD;
 }
 
index 124ebc4..6ffa267 100644 (file)
@@ -1663,7 +1663,7 @@ FCIMPL3(PCODE, COMDelegate::AdjustTarget, Object* refThisUNSAFE, Object* targetU
                 // <TODO>it looks like we need to pass an ownerType in here.
                 //  Why can we take a delegate to an interface method anyway?  </TODO>
                 // 
-                pCorrectedMethod = pMTTarg->FindDispatchSlotForInterfaceMD(pCorrectedMethod).GetMethodDesc();
+                pCorrectedMethod = pMTTarg->FindDispatchSlotForInterfaceMD(pCorrectedMethod, TRUE /* throwOnConflict */).GetMethodDesc();
                 _ASSERTE(pCorrectedMethod != NULL);
             }
         }
@@ -1836,7 +1836,7 @@ FCIMPL3(void, COMDelegate::DelegateConstruct, Object* refThisUNSAFE, Object* tar
                             // <TODO>it looks like we need to pass an ownerType in here.
                             //  Why can we take a delegate to an interface method anyway?  </TODO>
                             // 
-                            MethodDesc * pDispatchSlotMD = pMTTarg->FindDispatchSlotForInterfaceMD(pMeth).GetMethodDesc();
+                            MethodDesc * pDispatchSlotMD = pMTTarg->FindDispatchSlotForInterfaceMD(pMeth, TRUE /* throwOnConflict */).GetMethodDesc();
                             if (pDispatchSlotMD == NULL)
                             {
                                 COMPlusThrow(kArgumentException, W("Arg_DlgtTargMeth"));
index f9cb612..7201e10 100644 (file)
@@ -294,24 +294,29 @@ MethodDesc *CustomMarshalerInfo::GetCustomMarshalerMD(EnumCustomMarshalerMethods
     {
         case CustomMarshalerMethods_MarshalNativeToManaged:
             pMD = pMT->GetMethodDescForInterfaceMethod(
-                       MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__MARSHAL_NATIVE_TO_MANAGED));  
+                       MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__MARSHAL_NATIVE_TO_MANAGED),
+                       TRUE /* throwOnConflict */);
             break;
         case CustomMarshalerMethods_MarshalManagedToNative:
             pMD = pMT->GetMethodDescForInterfaceMethod(
-                       MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__MARSHAL_MANAGED_TO_NATIVE));
+                       MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__MARSHAL_MANAGED_TO_NATIVE),
+                       TRUE /* throwOnConflict */);
             break;
         case CustomMarshalerMethods_CleanUpNativeData:
             pMD = pMT->GetMethodDescForInterfaceMethod(
-                        MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__CLEANUP_NATIVE_DATA));
+                        MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__CLEANUP_NATIVE_DATA),
+                        TRUE /* throwOnConflict */);
             break;
 
         case CustomMarshalerMethods_CleanUpManagedData:
             pMD = pMT->GetMethodDescForInterfaceMethod(
-                        MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__CLEANUP_MANAGED_DATA));
+                        MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__CLEANUP_MANAGED_DATA),
+                        TRUE /* throwOnConflict */);
             break;
         case CustomMarshalerMethods_GetNativeDataSize:
             pMD = pMT->GetMethodDescForInterfaceMethod(
-                        MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__GET_NATIVE_DATA_SIZE));
+                        MscorlibBinder::GetMethod(METHOD__ICUSTOM_MARSHALER__GET_NATIVE_DATA_SIZE),
+                        TRUE /* throwOnConflict */);
             break;
         case CustomMarshalerMethods_GetInstance:
             // Must look this up by name since it's static
index 393c2aa..fe79ac6 100644 (file)
@@ -2702,7 +2702,7 @@ MethodDesc* DispatchInfo::GetCustomAttrProviderMD(TypeHandle hndCustomAttrProvid
     CONTRACT_END;
 
     MethodTable *pMT = hndCustomAttrProvider.AsMethodTable();
-    MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(METHOD__ICUSTOM_ATTR_PROVIDER__GET_CUSTOM_ATTRIBUTES));
+    MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(METHOD__ICUSTOM_ATTR_PROVIDER__GET_CUSTOM_ATTRIBUTES), TRUE /* throwOnConflict */);
 
     // Return the specified method desc.
     RETURN pMD;
@@ -3542,7 +3542,7 @@ MethodDesc* DispatchExInfo::GetIReflectMD(BinderMethodID Method)
     CONTRACT_END;
 
     MethodTable *pMT = m_pSimpleWrapperOwner->GetMethodTable();
-    MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(Method));
+    MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(Method), TRUE /* throwOnConflict */);
 
     // Return the specified method desc.
     RETURN pMD;
@@ -3562,7 +3562,7 @@ MethodDesc* DispatchExInfo::GetIExpandoMD(BinderMethodID Method)
     CONTRACT_END;
 
     MethodTable *pMT = m_pSimpleWrapperOwner->GetMethodTable();
-    MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(Method));
+    MethodDesc *pMD = pMT->GetMethodDescForInterfaceMethod(MscorlibBinder::GetMethod(Method), TRUE /* throwOnConflict */);
 
     // Return the specified method desc.
     RETURN pMD;
index 4423c98..51ab2ce 100644 (file)
@@ -8850,29 +8850,11 @@ CORINFO_METHOD_HANDLE CEEInfo::resolveVirtualMethodHelper(CORINFO_METHOD_HANDLE
                 pOwnerMT = pOwnerMT->GetCanonicalMethodTable();
             }
 
-            // In a try block because the interface method resolution might end up being
-            // ambiguous (diamond inheritance case of default interface methods).
-            EX_TRY
-            {
-                pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(TypeHandle(pOwnerMT), pBaseMD);
-            }
-            EX_CATCH
-            {
-            }
-            EX_END_CATCH(RethrowTransientExceptions)
+            pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(TypeHandle(pOwnerMT), pBaseMD, FALSE /* throwOnConflict */);
         }
         else if (!pBaseMD->HasClassOrMethodInstantiation())
         {
-            // In a try block because the interface method resolution might end up being
-            // ambiguous (diamond inheritance case of default interface methods).
-            EX_TRY
-            {
-                pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(pBaseMD);
-            }
-            EX_CATCH
-            {
-            }
-            EX_END_CATCH(RethrowTransientExceptions)
+            pDevirtMD = pDerivedMT->GetMethodDescForInterfaceMethod(pBaseMD, FALSE /* throwOnConflict */);
         }
         
         if (pDevirtMD == nullptr)
index 57f2495..b80f1e8 100644 (file)
@@ -799,7 +799,7 @@ PTR_MethodTable InterfaceInfo_t::GetApproxMethodTable(Module * pContainingModule
         TypeHandle resulTypeHnd = resultTypeObj->GetType();
         MethodTable *pResultMT = resulTypeHnd.GetMethodTable();
 
-        RETURN(pResultMT->GetMethodDescForInterfaceMethod(ownerType, pItfMD));
+        RETURN(pResultMT->GetMethodDescForInterfaceMethod(ownerType, pItfMD, TRUE /* throwOnConflict */));
     }
 #endif    
 
@@ -820,7 +820,7 @@ PTR_MethodTable InterfaceInfo_t::GetApproxMethodTable(Module * pContainingModule
 #endif // !FEATURE_COMINTEROP
 
     // Handle pure COM+ types.
-    RETURN (pServerMT->GetMethodDescForInterfaceMethod(ownerType, pItfMD));
+    RETURN (pServerMT->GetMethodDescForInterfaceMethod(ownerType, pItfMD, TRUE /* throwOnConflict */));
 }
 
 #ifdef FEATURE_COMINTEROP
@@ -858,7 +858,7 @@ MethodDesc *MethodTable::GetMethodDescForComInterfaceMethod(MethodDesc *pItfMD,
 
         // Calling GetTarget here instead of FindDispatchImpl gives us caching functionality to increase speed.
         PCODE tgt = VirtualCallStubManager::GetTarget(
-            pItfMT->GetLoaderAllocator()->GetDispatchToken(pItfMT->GetTypeID(), pItfMD->GetSlot()), this);
+            pItfMT->GetLoaderAllocator()->GetDispatchToken(pItfMT->GetTypeID(), pItfMD->GetSlot()), this, TRUE /* throwOnConflict */);
 
         if (tgt != NULL)
         {
@@ -2076,7 +2076,7 @@ BOOL MethodTable::ImplementsEquivalentInterface(MethodTable *pInterface)
 }
 
 //==========================================================================================
-MethodDesc *MethodTable::GetMethodDescForInterfaceMethod(MethodDesc *pInterfaceMD)
+MethodDesc *MethodTable::GetMethodDescForInterfaceMethod(MethodDesc *pInterfaceMD, BOOL throwOnConflict)
 {
     CONTRACTL
     {
@@ -2087,11 +2087,11 @@ MethodDesc *MethodTable::GetMethodDescForInterfaceMethod(MethodDesc *pInterfaceM
     CONTRACTL_END;
     WRAPPER_NO_CONTRACT;
 
-    return GetMethodDescForInterfaceMethod(TypeHandle(pInterfaceMD->GetMethodTable()), pInterfaceMD);
+    return GetMethodDescForInterfaceMethod(TypeHandle(pInterfaceMD->GetMethodTable()), pInterfaceMD, throwOnConflict);
 }
 
 //==========================================================================================
-MethodDesc *MethodTable::GetMethodDescForInterfaceMethod(TypeHandle ownerType, MethodDesc *pInterfaceMD)
+MethodDesc *MethodTable::GetMethodDescForInterfaceMethod(TypeHandle ownerType, MethodDesc *pInterfaceMD, BOOL throwOnConflict)
 {
     CONTRACTL
     {
@@ -2109,17 +2109,27 @@ MethodDesc *MethodTable::GetMethodDescForInterfaceMethod(TypeHandle ownerType, M
     MethodTable *pInterfaceMT = ownerType.AsMethodTable();
 
 #ifdef CROSSGEN_COMPILE
-    DispatchSlot implSlot(FindDispatchSlot(pInterfaceMT->GetTypeID(), pInterfaceMD->GetSlot()));
+    DispatchSlot implSlot(FindDispatchSlot(pInterfaceMT->GetTypeID(), pInterfaceMD->GetSlot(), throwOnConflict));
+    if (implSlot.IsNull())
+    {
+        _ASSERTE(!throwOnConflict);
+        return NULL;
+    }
     PCODE pTgt = implSlot.GetTarget();
 #else
     PCODE pTgt = VirtualCallStubManager::GetTarget(
         pInterfaceMT->GetLoaderAllocator()->GetDispatchToken(pInterfaceMT->GetTypeID(), pInterfaceMD->GetSlot()),
-        this);
+        this, throwOnConflict);
+    if (pTgt == NULL)
+    {
+        _ASSERTE(!throwOnConflict);
+        return NULL;
+    }
 #endif
     pMD = MethodTable::GetMethodDescForSlotAddress(pTgt);
 
 #ifdef _DEBUG
-    MethodDesc *pDispSlotMD = FindDispatchSlotForInterfaceMD(ownerType, pInterfaceMD).GetMethodDesc();
+    MethodDesc *pDispSlotMD = FindDispatchSlotForInterfaceMD(ownerType, pInterfaceMD, throwOnConflict).GetMethodDesc();
     _ASSERTE(pDispSlotMD == pMD);
 #endif // _DEBUG
 
@@ -6918,7 +6928,8 @@ BOOL
 MethodTable::FindDispatchImpl(
     UINT32         typeID, 
     UINT32         slotNumber, 
-    DispatchSlot * pImplSlot)
+    DispatchSlot * pImplSlot,
+    BOOL           throwOnConflict)
 {
     CONTRACT (BOOL) {
         INSTANCE_CHECK;
@@ -7001,7 +7012,8 @@ MethodTable::FindDispatchImpl(
                 if (FindDefaultInterfaceImplementation(
                     pIfcMD,     // the interface method being resolved
                     pIfcMT,     // the interface being resolved
-                    &pDefaultMethod))
+                    &pDefaultMethod,
+                    throwOnConflict))
                 {
                     // Now, construct a DispatchSlot to return in *pImplSlot
                     DispatchSlot ds(pDefaultMethod->GetMethodEntryPoint());
@@ -7080,7 +7092,8 @@ void ThrowExceptionForConflictingOverride(
 BOOL MethodTable::FindDefaultInterfaceImplementation(
     MethodDesc *pInterfaceMD,
     MethodTable *pInterfaceMT,
-    MethodDesc **ppDefaultMethod
+    MethodDesc **ppDefaultMethod,
+    BOOL throwOnConflict
 )
 {
     CONTRACT(BOOL) {
@@ -7313,7 +7326,11 @@ BOOL MethodTable::FindDefaultInterfaceImplementation(
         }
         else if (pBestCandidateMT != candidates[i].pMT)
         {
-            ThrowExceptionForConflictingOverride(this, pInterfaceMT, pInterfaceMD);
+            if (throwOnConflict)
+                ThrowExceptionForConflictingOverride(this, pInterfaceMT, pInterfaceMD);
+
+            *ppDefaultMethod = NULL;
+            RETURN(FALSE);
         }
     }
 
@@ -7331,17 +7348,17 @@ BOOL MethodTable::FindDefaultInterfaceImplementation(
 #endif // DACCESS_COMPILE
 
 //==========================================================================================
-DispatchSlot MethodTable::FindDispatchSlot(UINT32 typeID, UINT32 slotNumber)
+DispatchSlot MethodTable::FindDispatchSlot(UINT32 typeID, UINT32 slotNumber, BOOL throwOnConflict)
 {
     WRAPPER_NO_CONTRACT;
     STATIC_CONTRACT_SO_TOLERANT;
     DispatchSlot implSlot(NULL);
-    FindDispatchImpl(typeID, slotNumber, &implSlot);
+    FindDispatchImpl(typeID, slotNumber, &implSlot, throwOnConflict);
     return implSlot;
 }
 
 //==========================================================================================
-DispatchSlot MethodTable::FindDispatchSlot(DispatchToken tok)
+DispatchSlot MethodTable::FindDispatchSlot(DispatchToken tok, BOOL throwOnConflict)
 {
     CONTRACTL
     {
@@ -7351,28 +7368,28 @@ DispatchSlot MethodTable::FindDispatchSlot(DispatchToken tok)
         MODE_ANY;
     }
     CONTRACTL_END;
-    return FindDispatchSlot(tok.GetTypeID(), tok.GetSlotNumber());
+    return FindDispatchSlot(tok.GetTypeID(), tok.GetSlotNumber(), throwOnConflict);
 }
 
 #ifndef DACCESS_COMPILE
 
 //==========================================================================================
-DispatchSlot MethodTable::FindDispatchSlotForInterfaceMD(MethodDesc *pMD)
+DispatchSlot MethodTable::FindDispatchSlotForInterfaceMD(MethodDesc *pMD, BOOL throwOnConflict)
 {
     WRAPPER_NO_CONTRACT;
     CONSISTENCY_CHECK(CheckPointer(pMD));
     CONSISTENCY_CHECK(pMD->IsInterface());
-    return FindDispatchSlotForInterfaceMD(TypeHandle(pMD->GetMethodTable()), pMD);
+    return FindDispatchSlotForInterfaceMD(TypeHandle(pMD->GetMethodTable()), pMD, throwOnConflict);
 }
 
 //==========================================================================================
-DispatchSlot MethodTable::FindDispatchSlotForInterfaceMD(TypeHandle ownerType, MethodDesc *pMD)
+DispatchSlot MethodTable::FindDispatchSlotForInterfaceMD(TypeHandle ownerType, MethodDesc *pMD, BOOL throwOnConflict)
 {
     WRAPPER_NO_CONTRACT;
     CONSISTENCY_CHECK(!ownerType.IsNull());
     CONSISTENCY_CHECK(CheckPointer(pMD));
     CONSISTENCY_CHECK(pMD->IsInterface());
-    return FindDispatchSlot(ownerType.GetMethodTable()->GetTypeID(), pMD->GetSlot());
+    return FindDispatchSlot(ownerType.GetMethodTable()->GetTypeID(), pMD->GetSlot(), throwOnConflict);
 }
 
 //==========================================================================================
@@ -9859,7 +9876,7 @@ MethodTable::TryResolveConstraintMethodApprox(
                 thInterfaceType.AsMethodTable()->GetCanonicalMethodTable())
             {
                 cPotentialMatchingInterfaces++;
-                pMD = pCanonMT->GetMethodDescForInterfaceMethod(thPotentialInterfaceType, pGenInterfaceMD);
+                pMD = pCanonMT->GetMethodDescForInterfaceMethod(thPotentialInterfaceType, pGenInterfaceMD, FALSE /* throwOnConflict */);
 
                 // See code:#TryResolveConstraintMethodApprox_DoNotReturnParentMethod
                 if ((pMD != NULL) && !pMD->GetMethodTable()->IsValueType())
@@ -9890,9 +9907,8 @@ MethodTable::TryResolveConstraintMethodApprox(
                 if (this->CanCastToInterface(pInterfaceMT))
                 {
                     // We can resolve to exact method
-                    pMD = this->GetMethodDescForInterfaceMethod(pInterfaceMT, pInterfaceMD);
-                    _ASSERTE(pMD != NULL);
-                    fIsExactMethodResolved = TRUE;
+                    pMD = this->GetMethodDescForInterfaceMethod(pInterfaceMT, pInterfaceMD, FALSE /* throwOnConflict */);
+                    fIsExactMethodResolved = pMD != NULL;
                 }
             }
 
@@ -9910,7 +9926,7 @@ MethodTable::TryResolveConstraintMethodApprox(
             // lookup at runtime, or when not sharing generic code).
             if (pCanonMT->CanCastToInterface(thInterfaceType.GetMethodTable()))
             {
-                pMD = pCanonMT->GetMethodDescForInterfaceMethod(thInterfaceType, pGenInterfaceMD);
+                pMD = pCanonMT->GetMethodDescForInterfaceMethod(thInterfaceType, pGenInterfaceMD, FALSE /* throwOnConflict */);
                 if (pMD == NULL)
                 {
                     LOG((LF_JIT, LL_INFO10000, "TryResolveConstraintMethodApprox: failed to find method desc for interface method\n"));
index 9b78dda..2367d38 100644 (file)
@@ -2200,8 +2200,8 @@ public:
     BOOL ImplementsInterface(MethodTable *pInterface);
     BOOL ImplementsEquivalentInterface(MethodTable *pInterface);
 
-    MethodDesc *GetMethodDescForInterfaceMethod(TypeHandle ownerType, MethodDesc *pInterfaceMD);
-    MethodDesc *GetMethodDescForInterfaceMethod(MethodDesc *pInterfaceMD); // You can only use this one for non-generic interfaces
+    MethodDesc *GetMethodDescForInterfaceMethod(TypeHandle ownerType, MethodDesc *pInterfaceMD, BOOL throwOnConflict);
+    MethodDesc *GetMethodDescForInterfaceMethod(MethodDesc *pInterfaceMD, BOOL throwOnConflict); // You can only use this one for non-generic interfaces
     
     //-------------------------------------------------------------------
     // INTERFACE MAP.  
@@ -2453,26 +2453,28 @@ public:
     BOOL FindDispatchImpl(
         UINT32         typeID, 
         UINT32         slotNumber, 
-        DispatchSlot * pImplSlot);
+        DispatchSlot * pImplSlot,
+        BOOL           throwOnConflict);
 
 
 #ifndef DACCESS_COMPILE
     BOOL FindDefaultInterfaceImplementation(
         MethodDesc *pInterfaceMD,
         MethodTable *pObjectMT,
-        MethodDesc **ppDefaultMethod);
+        MethodDesc **ppDefaultMethod,
+        BOOL throwOnConflict);
 #endif // DACCESS_COMPILE
 
-    DispatchSlot FindDispatchSlot(UINT32 typeID, UINT32 slotNumber);
+    DispatchSlot FindDispatchSlot(UINT32 typeID, UINT32 slotNumber, BOOL throwOnConflict);
 
-    DispatchSlot FindDispatchSlot(DispatchToken tok);
+    DispatchSlot FindDispatchSlot(DispatchToken tok, BOOL throwOnConflict);
 
     // You must use the second of these two if there is any chance the pMD is a method
     // on a generic interface such as IComparable<T> (which it normally can be).  The 
     // ownerType is used to provide an exact qualification in the case the pMD is
     // a shared method descriptor.
-    DispatchSlot FindDispatchSlotForInterfaceMD(MethodDesc *pMD);
-    DispatchSlot FindDispatchSlotForInterfaceMD(TypeHandle ownerType, MethodDesc *pMD);
+    DispatchSlot FindDispatchSlotForInterfaceMD(MethodDesc *pMD, BOOL throwOnConflict);
+    DispatchSlot FindDispatchSlotForInterfaceMD(TypeHandle ownerType, MethodDesc *pMD, BOOL throwOnConflict);
 
     MethodDesc *ReverseInterfaceMDLookup(UINT32 slotNumber);
 
index a8a2d31..913ca1f 100644 (file)
@@ -1179,7 +1179,7 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementation(Enregi
         // with at least an abstract method. b19897_GetInterfaceMap_Abstract.exe tests this case.
         //@TODO:STUBDISPATCH: Don't need to track down the implementation, just the declaration, and this can
         //@TODO:              be done faster - just need to make a function FindDispatchDecl.
-        DispatchSlot slot(typeHandle.GetMethodTable()->FindDispatchSlotForInterfaceMD(thOwnerOfMD, pMD));
+        DispatchSlot slot(typeHandle.GetMethodTable()->FindDispatchSlotForInterfaceMD(thOwnerOfMD, pMD, TRUE /* throwOnConflict */));
     if (!slot.IsNull())
             pResult = slot.GetMethodDesc();
 
index 78a3748..c6fa852 100644 (file)
@@ -1911,7 +1911,7 @@ PCODE VirtualCallStubManager::ResolveWorker(StubCallSite* pCallSite,
     if (target == NULL)
     {
         CONSISTENCY_CHECK(stub == CALL_STUB_EMPTY_ENTRY);
-        patch = Resolver(objectType, token, protectedObj, &target);
+        patch = Resolver(objectType, token, protectedObj, &target, TRUE /* throwOnConflict */);
 
 #if defined(_DEBUG) 
         if ( !objectType->IsComObjectType() &&
@@ -2196,7 +2196,8 @@ VirtualCallStubManager::Resolver(
     MethodTable * pMT, 
     DispatchToken token,
     OBJECTREF   * protectedObj, // this one can actually be NULL, consider using pMT is you don't need the object itself
-    PCODE *       ppTarget)
+    PCODE *       ppTarget,
+    BOOL          throwOnConflict)
 {
     CONTRACTL {
         THROWS;
@@ -2211,7 +2212,7 @@ VirtualCallStubManager::Resolver(
     if (token.IsTypedToken())
     {
         dbg_pTokenMT = GetThread()->GetDomain()->LookupType(token.GetTypeID());
-        dbg_pTokenMD = dbg_pTokenMT->FindDispatchSlot(token.GetSlotNumber()).GetMethodDesc();
+        dbg_pTokenMD = dbg_pTokenMT->FindDispatchSlot(token.GetSlotNumber(), throwOnConflict).GetMethodDesc();
     }
 #endif // _DEBUG
 
@@ -2226,7 +2227,7 @@ VirtualCallStubManager::Resolver(
 
     MethodDesc * pMD = NULL;
     BOOL fShouldPatch = FALSE;
-    DispatchSlot implSlot(pMT->FindDispatchSlot(token));
+    DispatchSlot implSlot(pMT->FindDispatchSlot(token, throwOnConflict));
 
     // If we found a target, then just figure out if we're allowed to create a stub around
     // this target and backpatch the callsite.
@@ -2310,7 +2311,7 @@ VirtualCallStubManager::Resolver(
     else if (pMT->IsComObjectType() && IsInterfaceToken(token))
     {
         MethodTable * pItfMT = GetTypeFromToken(token);
-        implSlot = pItfMT->FindDispatchSlot(token.GetSlotNumber());
+        implSlot = pItfMT->FindDispatchSlot(token.GetSlotNumber(), throwOnConflict);
 
         if (pItfMT->HasInstantiation())
         {
@@ -2370,7 +2371,7 @@ VirtualCallStubManager::Resolver(
         TypeHandle resulTypeHnd = resultTypeObj->GetType();
         MethodTable *pResultMT = resulTypeHnd.GetMethodTable();
 
-        return Resolver(pResultMT, token, protectedObj, ppTarget);
+        return Resolver(pResultMT, token, protectedObj, ppTarget, throwOnConflict);
     }
 #endif // FEATURE_ICASTABLE
 
@@ -2381,7 +2382,7 @@ VirtualCallStubManager::Resolver(
         if (token.IsTypedToken())
         {
             pTokenMT = GetThread()->GetDomain()->LookupType(token.GetTypeID());
-            pTokenMD = pTokenMT->FindDispatchSlot(token.GetSlotNumber()).GetMethodDesc();
+            pTokenMD = pTokenMT->FindDispatchSlot(token.GetSlotNumber(), throwOnConflict).GetMethodDesc();
         }
 
 #ifdef FEATURE_COMINTEROP
@@ -2395,6 +2396,13 @@ VirtualCallStubManager::Resolver(
         }
         else
 #endif // FEATURE_COMINTEROP
+        if (!throwOnConflict)
+        {
+            // Assume we got null because there was a default interface method conflict
+            *ppTarget = NULL;
+            return FALSE;
+        }
+        else
         {
             // Method not found, and this should never happen for anything but equivalent types
             CONSISTENCY_CHECK(!implSlot.IsNull() && "Valid method implementation was not found.");
@@ -2532,7 +2540,8 @@ PCODE VirtualCallStubManager::CacheLookup(size_t token, UINT16 tokenHash, Method
 PCODE
 VirtualCallStubManager::GetTarget(
     DispatchToken token, 
-    MethodTable * pMT)
+    MethodTable * pMT,
+    BOOL throwOnConflict)
 {
     CONTRACTL {
         THROWS;
@@ -2563,8 +2572,8 @@ VirtualCallStubManager::GetTarget(
 
     // TODO: passing NULL as protectedObj here can lead to incorrect behavior for ICastable objects
     // We need to review if this is the case and refactor this code if we want ICastable to become officially supported    
-    fPatch = Resolver(pMT, token, NULL, &target);
-    _ASSERTE(target != NULL);
+    fPatch = Resolver(pMT, token, NULL, &target, throwOnConflict);
+    _ASSERTE(!throwOnConflict || target != NULL);
 
 #ifndef STUB_DISPATCH_PORTABLE
     if (fPatch)
@@ -2624,13 +2633,13 @@ VirtualCallStubManager::TraceResolver(
     CONSISTENCY_CHECK(CheckPointer(pMT));
 
 
-    DispatchSlot slot(pMT->FindDispatchSlot(token));
+    DispatchSlot slot(pMT->FindDispatchSlot(token, TRUE /* throwOnConflict */));
 
     if (slot.IsNull() && IsInterfaceToken(token) && pMT->IsComObjectType())
     {
         MethodDesc * pItfMD = GetInterfaceMethodDescFromToken(token);
         CONSISTENCY_CHECK(pItfMD->GetMethodTable()->GetSlot(pItfMD->GetSlot()) == pItfMD->GetMethodEntryPoint());
-        slot = pItfMD->GetMethodTable()->FindDispatchSlot(pItfMD->GetSlot());
+        slot = pItfMD->GetMethodTable()->FindDispatchSlot(pItfMD->GetSlot(), TRUE /* throwOnConflict */);
     }
 
     return (StubManager::TraceStub(slot.GetTarget(), trace));
@@ -4148,7 +4157,7 @@ MethodDesc *VirtualCallStubManagerManager::Entry2MethodDesc(
     PCODE target = NULL;
     // TODO: passing NULL as protectedObj here can lead to incorrect behavior for ICastable objects
     // We need to review if this is the case and refactor this code if we want ICastable to become officially supported
-    VirtualCallStubManager::Resolver(pMT, token, NULL, &target);
+    VirtualCallStubManager::Resolver(pMT, token, NULL, &target, TRUE /* throwOnConflict */);
 
     return pMT->GetMethodDescForSlotAddress(target);
 }
index 1d22e46..360f04a 100644 (file)
@@ -539,7 +539,8 @@ private:
     static BOOL Resolver(MethodTable   * pMT,
                          DispatchToken   token,
                          OBJECTREF     * protectedObj,
-                         PCODE         * ppTarget);
+                         PCODE         * ppTarget,
+                         BOOL          throwOnConflict);
 
     // This can be used to find a target without needing the ability to throw
     static BOOL TraceResolver(Object *pObj, DispatchToken token, TraceDestination *trace);
@@ -561,7 +562,7 @@ public:
     static PCODE CacheLookup(size_t token, UINT16 tokenHash, MethodTable *pMT);
 
     // Full exhaustive lookup. THROWS, GC_TRIGGERS
-    static PCODE GetTarget(DispatchToken token, MethodTable *pMT);
+    static PCODE GetTarget(DispatchToken token, MethodTable *pMT, BOOL throwOnConflict);
 
 private:
     // Given a dispatch token, return true if the token represents an interface, false if just a slot.
diff --git a/tests/src/Regressions/coreclr/16123/ambiguousconstraint.il b/tests/src/Regressions/coreclr/16123/ambiguousconstraint.il
new file mode 100644 (file)
index 0000000..0d17c00
--- /dev/null
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern System.Runtime { }
+
+.assembly ambiguousconstraint { }
+
+.class interface private abstract auto ansi IFoo
+{
+  .method public hidebysig newslot abstract virtual instance void Frob() cil managed
+  {
+  }
+}
+
+.class interface private abstract auto ansi IBar
+       implements IFoo
+{
+  .method public hidebysig newslot virtual final instance void Frob() cil managed
+  {
+    .override IFoo::Frob
+    ret
+  }
+}
+
+.class interface private abstract auto ansi IBaz
+       implements IFoo
+{
+  .method public hidebysig newslot virtual final instance void Frob() cil managed
+  {
+    .override IFoo::Frob
+    ret
+  }
+}
+
+.class private auto ansi beforefieldinit Fooer
+       extends [System.Runtime]System.ValueType
+       implements IBar, IBaz
+{
+}
+
+.class private auto ansi beforefieldinit Gen`1<(IFoo) T>
+       extends [System.Runtime]System.Object
+{
+  .method public hidebysig static int32 Check(!T x) cil managed
+  {
+    .try
+    {
+      ldarga.s 0
+      constrained. !T
+      callvirt instance void IFoo::Frob()
+      leave.s Fail
+    }
+    catch [System.Runtime]System.NotSupportedException
+    {
+      pop
+      leave.s Success
+    }
+  Fail:
+    ldc.i4 -1
+    ret
+  Success:
+    ldc.i4 100
+    ret
+  }
+}
+
+.method public hidebysig static int32 Main() cil managed
+{
+  .entrypoint
+  .locals init (valuetype Fooer)
+  ldloc.0
+  call int32 class Gen`1<valuetype Fooer>::Check(!0)
+  ret
+}
\ No newline at end of file
diff --git a/tests/src/Regressions/coreclr/16123/ambiguousconstraint.ilproj b/tests/src/Regressions/coreclr/16123/ambiguousconstraint.ilproj
new file mode 100644 (file)
index 0000000..86c222a
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{85DFC527-4DB1-595E-A7D7-E94EE1F8140D}</ProjectGuid>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <ReferenceLocalMscorlib>true</ReferenceLocalMscorlib>
+    <OutputType>Exe</OutputType>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Include="ambiguousconstraint.il" />
+  </ItemGroup>
+
+
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
\ No newline at end of file