The existing logic looking at MethodImpls to find the default interface implementation was not correct in the presence of generics. The MethodImpl records we build during type load only contain inexact MethodDescs for the declMethod that are not suitable for comparisons.
The fix is to pipe through the token and resolve the declaring method from it at the time of dispatch.
if(size > 0) {
// An array of DWORDs, the first entry representing count, and the rest representing slot numbers
S_SIZE_T cbCountAndSlots = S_SIZE_T(sizeof(DWORD)) + // DWORD for the total count of slots
- S_SIZE_T(size) * S_SIZE_T(sizeof(DWORD)); // DWORD each for the slot numbers
+ S_SIZE_T(size) * S_SIZE_T(sizeof(DWORD)) + // DWORD each for the slot numbers
+ S_SIZE_T(size) * S_SIZE_T(sizeof(mdToken)); // Token each for the method tokens
// MethodDesc* for each of the implemented methods
S_SIZE_T cbMethodDescs = S_SIZE_T(size) * S_SIZE_T(sizeof(RelativePointer<MethodDesc *>));
}
///////////////////////////////////////////////////////////////////////////////////////
-void MethodImpl::SetData(DWORD* slots, RelativePointer<MethodDesc*>* md)
+void MethodImpl::SetData(DWORD* slots, mdToken* tokens, RelativePointer<MethodDesc*>* md)
{
CONTRACTL {
NOTHROW;
DWORD *pdwSize = pdwSlots.GetValue();
DWORD dwSize = *pdwSize;
memcpy(&(pdwSize[1]), slots, dwSize*sizeof(DWORD));
+
+ // Copy tokens that correspond to the slots above
+ memcpy(&(pdwSize[1 + dwSize]), tokens, dwSize*sizeof(mdToken));
RelativePointer<MethodDesc *> *pImplMD = pImplementedMD.GetValue();
DWORD size = GetSize();
_ASSERTE(size > 0);
- image->StoreStructure(pdwSlots.GetValue(), (size+1)*sizeof(DWORD),
+ image->StoreStructure(pdwSlots.GetValue(), (size+1)*sizeof(DWORD)+size*sizeof(mdToken),
DataImage::ITEM_METHOD_DESC_COLD,
sizeof(DWORD));
image->StoreStructure(pImplementedMD.GetValue(), size*sizeof(RelativePointer<MethodDesc*>),
{
ULONG32 numSlots = GetSize();
DacEnumMemoryRegion(dac_cast<TADDR>(GetSlotsRawNonNull()),
- (numSlots + 1) * sizeof(DWORD));
+ (numSlots + 1) * sizeof(DWORD) + numSlots * sizeof(mdToken));
if (GetImpMDs().IsValid())
{
friend class NativeImageDumper;
#endif
- RelativePointer<PTR_DWORD> pdwSlots; // Maintains the slots in sorted order, the first entry is the size
+ RelativePointer<PTR_DWORD> pdwSlots; // Maintains the slots and tokens in sorted order, the first entry is the size
RelativePointer<DPTR( RelativePointer<PTR_MethodDesc> )> pImplementedMD;
public:
{ WRAPPER_NO_CONTRACT; if (IsValid()) m_iCur++; }
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 mdToken GetToken()
+ { WRAPPER_NO_CONTRACT; CONSISTENCY_CHECK(IsValid()); return m_pImpl->GetTokens()[m_iCur]; }
inline MethodDesc *GetMethodDesc()
{ WRAPPER_NO_CONTRACT; return m_pImpl->GetMethodDesc(m_iCur, (PTR_MethodDesc) m_pMD); }
};
#ifndef DACCESS_COMPILE
///////////////////////////////////////////////////////////////////////////////////////
+ inline mdToken* GetTokens()
+ {
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ if (pdwSlots.IsNull())
+ return NULL;
+ else
+ return (mdToken*)(GetSlotsRawNonNull() + 1 + *GetSlotsRawNonNull());
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////
void SetSize(LoaderHeap *pHeap, AllocMemTracker *pamTracker, DWORD size);
///////////////////////////////////////////////////////////////////////////////////////
- void SetData(DWORD* slots, RelativePointer<MethodDesc*> * md);
+ void SetData(DWORD* slots, mdToken* tokens, RelativePointer<MethodDesc*> * md);
#endif // !DACCESS_COMPILE
{
if (pCurMT->HasSameTypeDefAs(pInterfaceMT))
{
- // Generic variance match - we'll instantiate pCurMD with the right type arguments later
- pCurMD = pInterfaceMD;
+ if (!pInterfaceMD->IsAbstract())
+ {
+ // Generic variance match - we'll instantiate pCurMD with the right type arguments later
+ pCurMD = pInterfaceMD;
+ }
}
else
{
// Implicit override in default interface methods are not allowed
//
MethodIterator methodIt(pCurMT);
- for (; methodIt.IsValid(); methodIt.Next())
+ for (; methodIt.IsValid() && pCurMD == NULL; methodIt.Next())
{
MethodDesc *pMD = methodIt.GetMethodDesc();
int targetSlot = pInterfaceMD->GetSlot();
- if (pMD->IsMethodImpl())
+ // If this is not a MethodImpl, it can't be implementing the method we're looking for
+ if (!pMD->IsMethodImpl())
+ continue;
+
+ // We have a MethodImpl - iterate over all the declarations it's implementing,
+ // looking for the interface method we need.
+ MethodImpl::Iterator it(pMD);
+ for (; it.IsValid() && pCurMD == NULL; it.Next())
{
- MethodImpl::Iterator it(pMD);
- for (; it.IsValid(); it.Next())
- {
- MethodDesc *pDeclMD = it.GetMethodDesc();
+ MethodDesc *pDeclMD = it.GetMethodDesc();
- if (pDeclMD->GetSlot() != targetSlot)
- continue;
+ // Is this the right slot?
+ 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)
+ // Is this the right interface?
+ if (!pDeclMD->HasSameMethodDefAs(pInterfaceMD))
+ continue;
+
+ if (pInterfaceMD->HasClassInstantiation())
+ {
+ // pInterfaceMD will be in the canonical form, so we need to check the specific
+ // instantiation against pInterfaceMT.
+ //
+ // The parent of pDeclMD is unreliable for this purpose because it may or
+ // may not be canonicalized. Let's go from the metadata.
+
+ SigTypeContext typeContext = SigTypeContext(pCurMT);
+
+ mdTypeRef tkParent;
+ IfFailThrow(pMD->GetModule()->GetMDImport()->GetParentToken(it.GetToken(), &tkParent));
+
+ MethodTable* pDeclMT = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(
+ pMD->GetModule(),
+ tkParent,
+ &typeContext).AsMethodTable();
+
+ // We do CanCastToInterface to also cover variance.
+ // We already know this is a method on the same type definition as the (generic)
+ // interface but we need to make sure the instantiations match.
+ if (pDeclMT->CanCastToInterface(pInterfaceMT))
{
- // Exact match override
+ // We have a match
pCurMD = pMD;
- break;
}
- }
+ }
+ else
+ {
+ // No generics involved. If the method definitions match, it's a match.
+ pCurMD = pMD;
+ }
}
}
}
continue;
// Otherwise, record the method impl discovery if the match is
- bmtMethodImpl->AddMethodImpl(*it, declMethod, GetStackingAllocator());
+ bmtMethodImpl->AddMethodImpl(*it, declMethod, bmtMetaData->rgMethodImplTokens[m].methodDecl, GetStackingAllocator());
}
if (!fMatchFound && bmtMetaData->rgMethodImplTokens[m].fThrowIfUnmatchedDuringInexactMethodImplProcessing)
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token());
}
- bmtMethodImpl->AddMethodImpl(*it, declMethod, GetStackingAllocator());
+ bmtMethodImpl->AddMethodImpl(*it, declMethod, mdDecl, GetStackingAllocator());
}
}
}
DWORD dwMaxSlotSize = IsInterface() ? bmtMethod->dwNumberMethodImpls : bmtVT->cVirtualSlots;
DWORD * slots = new (&GetThread()->m_MarshalAlloc) DWORD[dwMaxSlotSize];
+ mdToken * tokens = new (&GetThread()->m_MarshalAlloc) mdToken[dwMaxSlotSize];
RelativePointer<MethodDesc *> * replaced = new (&GetThread()->m_MarshalAlloc) RelativePointer<MethodDesc*>[dwMaxSlotSize];
DWORD iEntry = 0;
while (true)
{ // collect information until we reach the next body
+ tokens[slotIndex] = bmtMethodImpl->GetDeclarationToken(iEntry);
+
// Get the declaration part of the method impl. It will either be a token
// (declaration is on this type) or a method desc.
bmtMethodHandle hDeclMethod = bmtMethodImpl->GetDeclarationMethod(iEntry);
if(iEntry == bmtMethodImpl->pIndex)
{
// We hit the end of the list so dump the current data and leave
- WriteMethodImplData(pCurImplMethod, slotIndex, slots, replaced);
+ WriteMethodImplData(pCurImplMethod, slotIndex, slots, tokens, replaced);
break;
}
else
if (pNextImplMethod != pCurImplMethod)
{
// If we're moving on to a new body, dump the current data and reset the counter
- WriteMethodImplData(pCurImplMethod, slotIndex, slots, replaced);
+ WriteMethodImplData(pCurImplMethod, slotIndex, slots, tokens, replaced);
slotIndex = 0;
}
bmtMDMethod * pImplMethod,
DWORD cSlots,
DWORD * rgSlots,
+ mdToken * rgTokens,
RelativePointer<MethodDesc *> * rgDeclMD)
{
STANDARD_VM_CONTRACT;
DWORD sTmp = rgSlots[i];
rgSlots[i] = rgSlots[min];
rgSlots[min] = sTmp;
+
+ mdToken tTmp = rgTokens[i];
+ rgTokens[i] = rgTokens[min];
+ rgTokens[min] = tTmp;
}
}
}
// Go and set the method impl
- pImpl->SetData(rgSlots, rgDeclMD);
+ pImpl->SetData(rgSlots, rgTokens, rgDeclMD);
GetHalfBakedClass()->SetContainsMethodImpls();
}
// 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)
+BOOL MethodTableBuilder::HasDefaultInterfaceImplementation(bmtRTType *pDeclType, MethodDesc *pDeclMD)
{
STANDARD_VM_CONTRACT;
#ifdef FEATURE_DEFAULT_INTERFACES
// If the interface method is already non-abstract, we are done
- if (pDeclMD->IsDefaultInterfaceMethod())
+ if (!pDeclMD->IsAbstract())
return TRUE;
- MethodTable *pDeclMT = pDeclMD->GetMethodTable();
+ int targetSlot = pDeclMD->GetSlot();
- // 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())
+ // Iterate over all the interfaces this type implements
+ bmtInterfaceEntry * pItfEntry = NULL;
+ for (DWORD i = 0; i < bmtInterface->dwInterfaceMapSize; i++)
{
- MethodTable *pIntfMT = intIt->GetInterfaceType()->GetMethodTable();
- if (pIntfMT->GetClass()->ContainsMethodImpls() && pIntfMT->CanCastToInterface(pDeclMT))
+ bmtRTType * pCurItf = bmtInterface->pInterfaceMap[i].GetInterfaceType();
+
+ // Go over the methods on the interface
+ MethodTable::IntroducedMethodIterator methIt(pCurItf->GetMethodTable());
+ for (; methIt.IsValid(); methIt.Next())
{
- MethodTable::MethodIterator methodIt(pIntfMT);
- for (; methodIt.IsValid(); methodIt.Next())
+ MethodDesc * pPotentialImpl = methIt.GetMethodDesc();
+
+ // If this interface method is not a MethodImpl, it can't possibly implement
+ // the interface method we are looking for
+ if (!pPotentialImpl->IsMethodImpl())
+ continue;
+
+ // Go over all the decls this MethodImpl is implementing
+ MethodImpl::Iterator it(pPotentialImpl);
+ for (; it.IsValid(); it.Next())
{
- MethodDesc *pMD = methodIt.GetMethodDesc();
- if (pMD->IsMethodImpl())
+ MethodDesc *pPotentialDecl = it.GetMethodDesc();
+
+ // Check this is a decl with the right slot
+ if (pPotentialDecl->GetSlot() != targetSlot)
+ continue;
+
+ // Find out what interface this default implementation is implementing
+ mdToken tkParent;
+ IfFailThrow(GetModule()->GetMDImport()->GetParentToken(it.GetToken(), &tkParent));
+
+ // We can only load the approximate interface at this point
+ MethodTable * pPotentialInterfaceMT = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(
+ GetModule(),
+ tkParent,
+ &bmtGenerics->typeContext,
+ ClassLoader::ThrowIfNotFound,
+ ClassLoader::PermitUninstDefOrRef,
+ ClassLoader::LoadTypes,
+ CLASS_LOAD_APPROXPARENTS,
+ TRUE).GetMethodTable()->GetCanonicalMethodTable();
+
+ // Is this a default implementation for the interface we are looking for?
+ if (pDeclType->GetMethodTable()->HasSameTypeDefAs(pPotentialInterfaceMT))
{
- MethodImpl::Iterator it(pMD);
- for (; it.IsValid(); it.Next())
+ // If the type is not generic, matching defs are all we need
+ if (!pDeclType->GetMethodTable()->HasInstantiation())
+ return TRUE;
+
+ // If this is generic, we need to compare under substitutions
+ Substitution curItfSubs(tkParent, GetModule(), &pCurItf->GetSubstitution());
+
+ // Type Equivalence is not respected for this comparision as you can have multiple type equivalent interfaces on a class
+ TokenPairList newVisited = TokenPairList::AdjustForTypeEquivalenceForbiddenScope(NULL);
+ if (MetaSig::CompareTypeDefsUnderSubstitutions(
+ pPotentialInterfaceMT, pDeclType->GetMethodTable(),
+ &curItfSubs, &pDeclType->GetSubstitution(),
+ &newVisited))
{
- if (it.GetMethodDesc() == pDeclMD)
- return TRUE;
+ return TRUE;
}
}
}
- }
+ }
}
#endif // FEATURE_DEFAULT_INTERFACES
{
MethodDesc *pMD = it.GetDeclMethodDesc();
- if (!HasDefaultInterfaceImplementation(pMD))
+ if (!HasDefaultInterfaceImplementation(intIt->GetInterfaceType(), pMD))
BuildMethodTableThrowException(IDS_CLASSLOAD_NOTIMPLEMENTED, pMD->GetNameOnNonArrayClass());
}
}
//*******************************************************************************
void MethodTableBuilder::bmtMethodImplInfo::AddMethodImpl(
- bmtMDMethod * pImplMethod, bmtMethodHandle declMethod,
+ bmtMDMethod * pImplMethod, bmtMethodHandle declMethod, mdToken declToken,
StackingAllocator * pStackingAllocator)
{
STANDARD_VM_CONTRACT;
rgEntries = rgEntriesNew;
cMaxIndex = newEntriesCount;
}
- rgEntries[pIndex++] = Entry(pImplMethod, declMethod);
+ rgEntries[pIndex++] = Entry(pImplMethod, declMethod, declToken);
}
//*******************************************************************************
{
bmtMethodHandle declMethod;
bmtMDMethod * pImplMethod;
+ mdToken declToken;
Entry(bmtMDMethod * pImplMethodIn,
- bmtMethodHandle declMethodIn)
+ bmtMethodHandle declMethodIn,
+ mdToken declToken)
: declMethod(declMethodIn),
- pImplMethod(pImplMethodIn)
+ pImplMethod(pImplMethodIn),
+ declToken(declToken)
{}
Entry()
: declMethod(),
- pImplMethod(NULL)
+ pImplMethod(NULL),
+ declToken()
{}
};
AddMethodImpl(
bmtMDMethod * pImplMethod,
bmtMethodHandle declMethod,
+ mdToken declToken,
StackingAllocator * pStackingAllocator);
//-----------------------------------------------------------------------------------------
{ LIMITED_METHOD_CONTRACT; _ASSERTE(i < pIndex); return rgEntries[i].declMethod; }
//-----------------------------------------------------------------------------------------
+ // Get the decl method for a particular methodimpl entry.
+ mdToken
+ GetDeclarationToken(
+ DWORD i)
+ { LIMITED_METHOD_CONTRACT; _ASSERTE(i < pIndex); return rgEntries[i].declToken; }
+
+ //-----------------------------------------------------------------------------------------
// Get the impl method for a particular methodimpl entry.
bmtMDMethod *
GetImplementationMethod(
bmtMDMethod * pImplMethod,
DWORD cSlots,
DWORD * rgSlots,
+ mdToken * rgTokens,
RelativePointer<MethodDesc *> * rgDeclMD);
// --------------------------------------------------------------------------------------------
VOID HandleGCForValueClasses(
MethodTable **);
- BOOL HasDefaultInterfaceImplementation(MethodDesc *pIntfMD);
+ BOOL HasDefaultInterfaceImplementation(bmtRTType *pIntfType, MethodDesc *pIntfMD);
VOID VerifyVirtualMethodsImplemented(MethodTable::MethodData * hMTData);
VOID CheckForTypeEquivalence(
--- /dev/null
+// 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.
+
+// Makes sure that a type that implements IFoo<string> through a default
+// interface method and declares it also implements IFoo<ValueType>
+// (but it actually doesn't) will not be loadable.
+
+.assembly extern System.Runtime { }
+
+.assembly notimplemented { }
+
+.class interface private abstract auto ansi IFoo`1<T>
+{
+ .method public hidebysig newslot abstract virtual instance void Frob() cil managed
+ {
+ }
+}
+
+.class interface private abstract auto ansi IBar
+ implements class IFoo`1<class [System.Runtime]System.String>
+{
+ .method public hidebysig newslot virtual final instance void Frob() cil managed
+ {
+ .override class IFoo`1<class [System.Runtime]System.String>::Frob
+ ret
+ }
+}
+
+.class private auto ansi beforefieldinit Fooer
+ extends [System.Runtime]System.Object
+ implements IBar, class IFoo`1<class [System.Runtime]System.ValueType>
+{
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
+
+.method private hidebysig static void LoadFooer() cil managed noinlining
+{
+ newobj instance void Fooer::.ctor()
+ pop
+ ret
+}
+
+.method public hidebysig static int32 Main() cil managed
+{
+ .entrypoint
+
+ .try
+ {
+ call void LoadFooer()
+ leave DidNotThrow
+ }
+ catch [System.Runtime]System.TypeLoadException
+ {
+ pop
+ leave Okay
+ }
+
+Okay:
+ ldc.i4 100
+ ret
+
+DidNotThrow:
+ ldc.i4.m1
+ ret
+}
--- /dev/null
+<?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="notimplemented.il" />
+ </ItemGroup>
+
+
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
--- /dev/null
+// 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 boring { }
+
+.class interface private abstract auto ansi IFoo`1<T>
+{
+ .method public hidebysig newslot abstract virtual instance int32 Frob() cil managed
+ {
+ }
+
+ .method public hidebysig newslot virtual instance int32 Bark() cil managed
+ {
+ ldc.i4.m1
+ ret
+ }
+}
+
+.class interface private abstract auto ansi IBar
+ implements class IFoo`1<class [System.Runtime]System.String>,
+ class IFoo`1<class [System.Runtime]System.Object>,
+ class IFoo`1<class [System.Runtime]System.ValueType>
+{
+ .method public hidebysig newslot virtual final instance int32 FrobImpl1() cil managed
+ {
+ .override class IFoo`1<class [System.Runtime]System.String>::Frob
+ .override class IFoo`1<class [System.Runtime]System.Object>::Frob
+ ldc.i4.1
+ ret
+ }
+
+ .method public hidebysig newslot virtual final instance int32 FrobImpl2() cil managed
+ {
+ .override class IFoo`1<class [System.Runtime]System.ValueType>::Frob
+ ldc.i4.2
+ ret
+ }
+
+ .method public hidebysig newslot virtual final instance int32 BarkImpl1() cil managed
+ {
+ .override class IFoo`1<class [System.Runtime]System.String>::Bark
+ ldc.i4.3
+ ret
+ }
+
+ .method public hidebysig newslot virtual final instance int32 BarkImpl2() cil managed
+ {
+ .override class IFoo`1<class [System.Runtime]System.ValueType>::Bark
+ ldc.i4.4
+ ret
+ }
+}
+
+.class private auto ansi beforefieldinit Fooer
+ extends [System.Runtime]System.Object
+ implements IBar
+{
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
+
+.method public hidebysig static int32 Main() cil managed
+{
+ .entrypoint
+
+ newobj instance void class Fooer::.ctor()
+ callvirt instance int32 class IFoo`1<string>::Frob()
+ ldc.i4.1
+ ceq
+ brtrue IFoo_String_Frob_Okay
+ ldc.i4.1
+ ret
+IFoo_String_Frob_Okay:
+
+ newobj instance void class Fooer::.ctor()
+ callvirt instance int32 class IFoo`1<object>::Frob()
+ ldc.i4.1
+ ceq
+ brtrue IFoo_Object_Frob_Okay
+ ldc.i4.2
+ ret
+IFoo_Object_Frob_Okay:
+
+ newobj instance void class Fooer::.ctor()
+ callvirt instance int32 class IFoo`1<class [System.Runtime]System.ValueType>::Frob()
+ ldc.i4.2
+ ceq
+ brtrue IFoo_ValueType_Frob_Okay
+ ldc.i4.3
+ ret
+IFoo_ValueType_Frob_Okay:
+
+ newobj instance void class Fooer::.ctor()
+ callvirt instance int32 class IFoo`1<string>::Bark()
+ ldc.i4.3
+ ceq
+ brtrue IFoo_String_Bark_Okay
+ ldc.i4.4
+ ret
+IFoo_String_Bark_Okay:
+
+ newobj instance void class Fooer::.ctor()
+ callvirt instance int32 class IFoo`1<object>::Bark()
+ ldc.i4.m1
+ ceq
+ brtrue IFoo_Object_Bark_Okay
+ ldc.i4.5
+ ret
+IFoo_Object_Bark_Okay:
+
+ newobj instance void class Fooer::.ctor()
+ callvirt instance int32 class IFoo`1<class [System.Runtime]System.ValueType>::Bark()
+ ldc.i4.4
+ ceq
+ brtrue IFoo_ValueType_Bark_Okay
+ ldc.i4.6
+ ret
+IFoo_ValueType_Bark_Okay:
+
+ ldc.i4 100
+ ret
+}
--- /dev/null
+<?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="boring.il" />
+ </ItemGroup>
+
+
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
--- /dev/null
+// 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 variance { }
+
+.class interface private abstract auto ansi IContravariant`1<- T>
+{
+ .method public hidebysig newslot abstract virtual instance valuetype [System.Runtime]System.RuntimeTypeHandle Frob(!T t) cil managed
+ {
+ }
+}
+
+.class interface private abstract auto ansi IBar`1<- T>
+ implements class IContravariant`1<!T>
+{
+ .method public hidebysig newslot virtual final instance valuetype [System.Runtime]System.RuntimeTypeHandle Frob(!T t) cil managed
+ {
+ .override class IContravariant`1<!T>::Frob
+ ldtoken !T
+ ret
+ }
+}
+
+
+.class private auto ansi beforefieldinit Fooer
+ extends [System.Runtime]System.Object
+ implements class IBar`1<class [System.Runtime]System.Object>
+{
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [System.Runtime]System.Object::.ctor()
+ ret
+ }
+}
+
+.method public hidebysig static int32 Main() cil managed
+{
+ .entrypoint
+ .locals (valuetype [System.Runtime]System.RuntimeTypeHandle)
+ newobj instance void class Fooer::.ctor()
+ ldnull
+ callvirt instance valuetype [System.Runtime]System.RuntimeTypeHandle class IContravariant`1<string>::Frob(!0)
+ stloc.0
+ ldloca 0
+ ldtoken object
+ call instance bool valuetype [System.Runtime]System.RuntimeTypeHandle::Equals(valuetype [System.Runtime]System.RuntimeTypeHandle)
+ brtrue Okay
+ ldc.i4.m1
+ ret
+
+Okay:
+ ldc.i4 100
+ ret
+}
--- /dev/null
+<?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="variance.il" />
+ </ItemGroup>
+
+
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>