summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Robinson <arobins@microsoft.com>2018-11-29 12:44:04 -0800
committerGitHub <noreply@github.com>2018-11-29 12:44:04 -0800
commit11d1b645f0dede73ded0030b56d7c506150b0741 (patch)
tree279600e72cf717e3133210765f99541afd59341f
parent211d963a42c8988770afa4d2edcbe9be0ed0b8a8 (diff)
downloadcoreclr-11d1b645f0dede73ded0030b56d7c506150b0741.tar.gz
coreclr-11d1b645f0dede73ded0030b56d7c506150b0741.tar.bz2
coreclr-11d1b645f0dede73ded0030b56d7c506150b0741.zip
Enable type equivalence (#21265)
* Enable TypeEquivalence feature for Windows platform * Basic test - verified test exercises TypeEquivalence code paths
-rw-r--r--clr.coreclr.props1
-rw-r--r--clr.defines.targets1
-rw-r--r--clrdefinitions.cmake3
-rw-r--r--src/vm/CMakeLists.txt2
-rw-r--r--src/vm/appdomain.cpp5
-rw-r--r--src/vm/class.h4
-rw-r--r--src/vm/crossgen/CMakeLists.txt2
-rw-r--r--src/vm/method.cpp11
-rw-r--r--src/vm/method.hpp15
-rw-r--r--src/vm/method.inl8
-rw-r--r--src/vm/methodtable.cpp74
-rw-r--r--src/vm/methodtable.h28
-rw-r--r--src/vm/methodtablebuilder.cpp7
-rw-r--r--src/vm/siginfo.cpp44
-rw-r--r--src/vm/typeequivalencehash.cpp4
-rw-r--r--src/vm/typeequivalencehash.hpp41
-rw-r--r--tests/src/baseservices/typeequivalence/TypeEquivalence.props9
-rw-r--r--tests/src/baseservices/typeequivalence/TypeEquivalence.targets23
-rw-r--r--tests/src/baseservices/typeequivalence/contracts/TypeContracts.csproj12
-rw-r--r--tests/src/baseservices/typeequivalence/contracts/Types.cs18
-rw-r--r--tests/src/baseservices/typeequivalence/impl/Impls.cs19
-rw-r--r--tests/src/baseservices/typeequivalence/impl/TypeImpl.csproj25
-rw-r--r--tests/src/baseservices/typeequivalence/simple/Simple.cs53
-rw-r--r--tests/src/baseservices/typeequivalence/simple/Simple.csproj27
24 files changed, 304 insertions, 132 deletions
diff --git a/clr.coreclr.props b/clr.coreclr.props
index 872cbbca83..6ebc3609b7 100644
--- a/clr.coreclr.props
+++ b/clr.coreclr.props
@@ -7,6 +7,7 @@
<FeatureManagedEtwChannels>true</FeatureManagedEtwChannels>
<FeatureManagedEtw>true</FeatureManagedEtw>
<FeaturePerfTracing>true</FeaturePerfTracing>
+ <FeatureTypeEquivalence>true</FeatureTypeEquivalence>
<ProfilingSupportedBuild>true</ProfilingSupportedBuild>
</PropertyGroup>
diff --git a/clr.defines.targets b/clr.defines.targets
index 32876ea3b7..b440cddd15 100644
--- a/clr.defines.targets
+++ b/clr.defines.targets
@@ -24,6 +24,7 @@
<DefineConstants Condition="'$(FeatureUseLcid)' == 'true'">$(DefineConstants);FEATURE_USE_LCID</DefineConstants>
<DefineConstants Condition="'$(FeatureWin32Registry)' == 'true'">$(DefineConstants);FEATURE_WIN32_REGISTRY</DefineConstants>
<DefineConstants Condition="'$(FeatureDefaultInterfaces)' == 'true'">$(DefineConstants);FEATURE_DEFAULT_INTERFACES</DefineConstants>
+ <DefineConstants Condition="'$(FeatureTypeEquivalence)' == 'true'">$(DefineConstants);FEATURE_TYPEEQUIVALENCE</DefineConstants>
<DefineConstants Condition="'$(ProfilingSupportedBuild)' == 'true'">$(DefineConstants);PROFILING_SUPPORTED</DefineConstants>
<DefineConstants Condition="'$(FeatureProfAttach)' == 'true'">$(DefineConstants);FEATURE_PROFAPI_ATTACH_DETACH</DefineConstants>
diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake
index 2007f0dc5f..11d2a43af7 100644
--- a/clrdefinitions.cmake
+++ b/clrdefinitions.cmake
@@ -204,6 +204,9 @@ endif(FEATURE_NGEN_RELOCS_OPTIMIZATIONS)
add_definitions(-DFEATURE_SVR_GC)
add_definitions(-DFEATURE_SYMDIFF)
add_definitions(-DFEATURE_TIERED_COMPILATION)
+if (WIN32)
+ add_definitions(-DFEATURE_TYPEEQUIVALENCE)
+endif(WIN32)
if (CLR_CMAKE_PLATFORM_ARCH_AMD64)
# Enable the AMD64 Unix struct passing JIT-EE interface for all AMD64 platforms, to enable altjit.
add_definitions(-DUNIX_AMD64_ABI_ITF)
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index 06532b30f2..c5873ee4ed 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -114,6 +114,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON
tieredcompilation.cpp
typectxt.cpp
typedesc.cpp
+ typeequivalencehash.cpp
typehandle.cpp
typehash.cpp
typestring.cpp
@@ -220,6 +221,7 @@ set(VM_HEADERS_DAC_AND_WKS_COMMON
typectxt.h
typedesc.h
typedesc.inl
+ typeequivalencehash.hpp
typehandle.h
typehandle.inl
typehash.h
diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp
index 0a5e9de23c..b0cb03bcb3 100644
--- a/src/vm/appdomain.cpp
+++ b/src/vm/appdomain.cpp
@@ -52,9 +52,8 @@
#include "rcwrefcache.h"
#include "olecontexthelpers.h"
#endif // FEATURE_COMINTEROP
-#ifdef FEATURE_TYPEEQUIVALENCE
+
#include "typeequivalencehash.hpp"
-#endif
#include "appdomain.inl"
#include "typeparse.h"
@@ -8826,7 +8825,7 @@ TypeEquivalenceHashTable * AppDomain::GetTypeEquivalenceCache()
#endif
if (m_pTypeEquivalenceTable.Load() == NULL)
{
- m_pTypeEquivalenceTable = TypeEquivalenceHashTable::Create(this, 12, &m_TypeEquivalenceCrst);
+ m_pTypeEquivalenceTable = TypeEquivalenceHashTable::Create(this, /* bucket count */ 12, &m_TypeEquivalenceCrst);
}
}
return m_pTypeEquivalenceTable;
diff --git a/src/vm/class.h b/src/vm/class.h
index 34aac07e3e..54253ea70d 100644
--- a/src/vm/class.h
+++ b/src/vm/class.h
@@ -1127,13 +1127,13 @@ public:
return m_VMFlags & VMFLAG_IS_EQUIVALENT_TYPE;
}
-#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_TYPEEQUIVALENCE
inline void SetIsEquivalentType()
{
LIMITED_METHOD_CONTRACT;
m_VMFlags |= VMFLAG_IS_EQUIVALENT_TYPE;
}
-#endif
+#endif // FEATURE_TYPEEQUIVALENCE
/*
* Number of static handles allocated
diff --git a/src/vm/crossgen/CMakeLists.txt b/src/vm/crossgen/CMakeLists.txt
index 1d1eb23dab..8c9bfc58c2 100644
--- a/src/vm/crossgen/CMakeLists.txt
+++ b/src/vm/crossgen/CMakeLists.txt
@@ -76,6 +76,7 @@ set(VM_CROSSGEN_SOURCES
../stublink.cpp
../typectxt.cpp
../typedesc.cpp
+ ../typeequivalencehash.cpp
../typehandle.cpp
../typehash.cpp
../typeparse.cpp
@@ -172,6 +173,7 @@ set(VM_CROSSGEN_HEADERS
../typectxt.h
../typedesc.h
../typedesc.inl
+ ../typeequivalencehash.hpp
../typehandle.h
../typehandle.inl
../typehash.h
diff --git a/src/vm/method.cpp b/src/vm/method.cpp
index 3c2ba7d17f..9c19c688ac 100644
--- a/src/vm/method.cpp
+++ b/src/vm/method.cpp
@@ -5407,7 +5407,7 @@ moveToNextToken:
#ifdef FEATURE_TYPEEQUIVALENCE
-void CheckForEquivalenceAndLoadType(Module *pModule, mdToken token, Module *pDefModule, mdToken defToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData)
+static void CheckForEquivalenceAndLoadType(Module *pModule, mdToken token, Module *pDefModule, mdToken defToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData)
{
CONTRACTL
{
@@ -5426,9 +5426,11 @@ void CheckForEquivalenceAndLoadType(Module *pModule, mdToken token, Module *pDef
TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, pTypeContext);
}
}
+#endif // FEATURE_TYPEEQUIVALENCE
BOOL MethodDesc::HasTypeEquivalentStructParameters()
{
+#ifdef FEATURE_TYPEEQUIVALENCE
CONTRACTL
{
THROWS;
@@ -5447,9 +5449,14 @@ BOOL MethodDesc::HasTypeEquivalentStructParameters()
SetDoesNotHaveEquivalentValuetypeParameters();
return fHasTypeEquivalentStructParameters;
-}
+
+#else
+ LIMITED_METHOD_CONTRACT;
+ return FALSE;
#endif // FEATURE_TYPEEQUIVALENCE
+}
+
PrecodeType MethodDesc::GetPrecodeType()
{
diff --git a/src/vm/method.hpp b/src/vm/method.hpp
index 529064d0ea..8b136e6fd3 100644
--- a/src/vm/method.hpp
+++ b/src/vm/method.hpp
@@ -1615,15 +1615,8 @@ public:
VOID GetMethodInfoNoSig(SString &namespaceOrClassName, SString &methodName);
VOID GetFullMethodInfo(SString& fullMethodSigName);
- BOOL HasTypeEquivalentStructParameters()
-#ifndef FEATURE_TYPEEQUIVALENCE
- {
- LIMITED_METHOD_CONTRACT;
- return FALSE;
- }
-#else
- ;
-#endif
+ BOOL HasTypeEquivalentStructParameters();
+
typedef void (*WalkValueTypeParameterFnPtr)(Module *pModule, mdToken token, Module *pDefModule, mdToken tkDefToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData);
void WalkValueTypeParameters(MethodTable *pMT, WalkValueTypeParameterFnPtr function, void *pData);
@@ -1749,7 +1742,7 @@ public:
WORD InterlockedUpdateFlags3(WORD wMask, BOOL fSet);
-#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_TYPEEQUIVALENCE
inline BOOL DoesNotHaveEquivalentValuetypeParameters()
{
LIMITED_METHOD_DAC_CONTRACT;
@@ -1761,7 +1754,7 @@ public:
LIMITED_METHOD_CONTRACT;
InterlockedUpdateFlags3(enum_flag3_DoesNotHaveEquivalentValuetypeParameters, TRUE);
}
-#endif //FEATURE_COMINTEROP
+#endif // FEATURE_TYPEEQUIVALENCE
inline BOOL HasForwardedValuetypeParameter()
{
diff --git a/src/vm/method.inl b/src/vm/method.inl
index ca9a17a715..9d55ae9260 100644
--- a/src/vm/method.inl
+++ b/src/vm/method.inl
@@ -170,14 +170,6 @@ inline ComPlusCallInfo *ComPlusCallInfo::FromMethodDesc(MethodDesc *pMD)
#endif //FEATURE_COMINTEROP
-#ifndef FEATURE_TYPEEQUIVALENCE
-inline BOOL HasTypeEquivalentStructParameters()
-{
- LIMITED_METHOD_CONTRACT;
- return FALSE;
-}
-#endif // FEATURE_TYPEEQUIVALENCE
-
#ifdef FEATURE_CODE_VERSIONING
inline CodeVersionManager * MethodDesc::GetCodeVersionManager()
{
diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp
index d83e62d450..5ed91d1fe2 100644
--- a/src/vm/methodtable.cpp
+++ b/src/vm/methodtable.cpp
@@ -56,9 +56,7 @@
#include "winrttypenameconverter.h"
#endif // FEATURE_COMINTEROP
-#ifdef FEATURE_TYPEEQUIVALENCE
#include "typeequivalencehash.hpp"
-#endif
#include "generics.h"
#include "genericdict.h"
@@ -604,14 +602,6 @@ void MethodTable::SetComObjectType()
SetFlag(enum_flag_ComObject);
}
-#if defined(FEATURE_TYPEEQUIVALENCE)
-void MethodTable::SetHasTypeEquivalence()
-{
- LIMITED_METHOD_CONTRACT;
- SetFlag(enum_flag_HasTypeEquivalence);
-}
-#endif
-
#ifdef FEATURE_ICASTABLE
void MethodTable::SetICastable()
{
@@ -1397,10 +1387,12 @@ BOOL MethodTable::IsEquivalentTo_WorkerInner(MethodTable *pOtherMT COMMA_INDEBUG
}
CONTRACTL_END;
+ TypeEquivalenceHashTable *typeHashTable = NULL;
AppDomain *pDomain = GetAppDomain();
if (pDomain != NULL)
{
- TypeEquivalenceHashTable::EquivalenceMatch match = pDomain->GetTypeEquivalenceCache()->CheckEquivalence(TypeHandle(this), TypeHandle(pOtherMT));
+ typeHashTable = pDomain->GetTypeEquivalenceCache();
+ TypeEquivalenceHashTable::EquivalenceMatch match = typeHashTable->CheckEquivalence(TypeHandle(this), TypeHandle(pOtherMT));
switch (match)
{
case TypeEquivalenceHashTable::Match:
@@ -1417,9 +1409,10 @@ BOOL MethodTable::IsEquivalentTo_WorkerInner(MethodTable *pOtherMT COMMA_INDEBUG
BOOL fEquivalent = FALSE;
+ // Check if type is generic
if (HasInstantiation())
{
- // we limit variance on generics only to interfaces
+ // Limit variance on generics only to interfaces
if (!IsInterface() || !pOtherMT->IsInterface())
{
fEquivalent = FALSE;
@@ -1430,12 +1423,14 @@ BOOL MethodTable::IsEquivalentTo_WorkerInner(MethodTable *pOtherMT COMMA_INDEBUG
Instantiation inst1 = GetInstantiation();
Instantiation inst2 = pOtherMT->GetInstantiation();
+ // Verify generic argument count
if (inst1.GetNumArgs() != inst2.GetNumArgs())
{
fEquivalent = FALSE;
goto EquivalenceCalculated;
}
+ // Verify each generic argument type
for (DWORD i = 0; i < inst1.GetNumArgs(); i++)
{
if (!inst1[i].IsEquivalentTo(inst2[i] COMMA_INDEBUG(pVisited)))
@@ -1467,22 +1462,23 @@ BOOL MethodTable::IsEquivalentTo_WorkerInner(MethodTable *pOtherMT COMMA_INDEBUG
}
// arrays of structures have their own unshared MTs and will take this path
- fEquivalent = (GetApproxArrayElementTypeHandle().IsEquivalentTo(pOtherMT->GetApproxArrayElementTypeHandle() COMMA_INDEBUG(pVisited)));
+ TypeHandle elementType1 = GetApproxArrayElementTypeHandle();
+ TypeHandle elementType2 = pOtherMT->GetApproxArrayElementTypeHandle();
+ fEquivalent = elementType1.IsEquivalentTo(elementType2 COMMA_INDEBUG(pVisited));
goto EquivalenceCalculated;
}
fEquivalent = CompareTypeDefsForEquivalence(GetCl(), pOtherMT->GetCl(), GetModule(), pOtherMT->GetModule(), NULL);
EquivalenceCalculated:
- // Only record equivalence matches if we are in an AppDomain
- if (pDomain != NULL)
+ // Record equivalence matches if a table exists
+ if (typeHashTable != NULL)
{
// Collectible type results will not get cached.
- if ((!this->Collectible() && !pOtherMT->Collectible()))
+ if ((!Collectible() && !pOtherMT->Collectible()))
{
- TypeEquivalenceHashTable::EquivalenceMatch match;
- match = fEquivalent ? TypeEquivalenceHashTable::Match : TypeEquivalenceHashTable::NoMatch;
- pDomain->GetTypeEquivalenceCache()->RecordEquivalence(TypeHandle(this), TypeHandle(pOtherMT), match);
+ auto match = fEquivalent ? TypeEquivalenceHashTable::Match : TypeEquivalenceHashTable::NoMatch;
+ typeHashTable->RecordEquivalence(TypeHandle(this), TypeHandle(pOtherMT), match);
}
}
@@ -5363,12 +5359,12 @@ VOID DoAccessibilityCheckForConstraints(MethodTable *pAskingMT, TypeVarTypeDesc
// Used so that we can have one valuetype walking algorithm used for type equivalence walking of the parameters of the method.
struct DoFullyLoadLocals
{
- DoFullyLoadLocals(DFLPendingList *pPendingParam, ClassLoadLevel levelParam, MethodTable *pMT, Generics::RecursionGraph *pVisited) :
- newVisited(pVisited, TypeHandle(pMT)),
- pPending(pPendingParam),
- level(levelParam),
- fBailed(FALSE)
-#ifdef FEATURE_COMINTEROP
+ DoFullyLoadLocals(DFLPendingList *pPendingParam, ClassLoadLevel levelParam, MethodTable *pMT, Generics::RecursionGraph *pVisited)
+ : newVisited(pVisited, TypeHandle(pMT))
+ , pPending(pPendingParam)
+ , level(levelParam)
+ , fBailed(FALSE)
+#ifdef FEATURE_TYPEEQUIVALENCE
, fHasEquivalentStructParameter(FALSE)
#endif
, fHasTypeForwarderDependentStructParameter(FALSE)
@@ -5381,7 +5377,7 @@ struct DoFullyLoadLocals
DFLPendingList * const pPending;
const ClassLoadLevel level;
BOOL fBailed;
-#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_TYPEEQUIVALENCE
BOOL fHasEquivalentStructParameter;
#endif
BOOL fHasTypeForwarderDependentStructParameter;
@@ -6677,7 +6673,7 @@ MethodTable *MethodTable::GetDefaultWinRTInterface()
#endif // !DACCESS_COMPILE
#endif // FEATURE_COMINTEROP
-#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_TYPEEQUIVALENCE
#ifndef DACCESS_COMPILE
WORD GetEquivalentMethodSlot(MethodTable * pOldMT, MethodTable * pNewMT, WORD wMTslot, BOOL *pfFound)
@@ -6687,20 +6683,15 @@ WORD GetEquivalentMethodSlot(MethodTable * pOldMT, MethodTable * pNewMT, WORD wM
GC_NOTRIGGER;
} CONTRACTL_END;
- MethodDesc * pMDRet = NULL;
*pfFound = FALSE;
+ WORD wVTslot = wMTslot;
+
+#ifdef FEATURE_COMINTEROP
// Get the COM vtable slot corresponding to the given MT slot
- WORD wVTslot;
if (pOldMT->IsSparseForCOMInterop())
- {
wVTslot = pOldMT->GetClass()->GetSparseCOMInteropVTableMap()->LookupVTSlot(wMTslot);
- }
- else
- {
- wVTslot = wMTslot;
- }
-
+
// If the other MT is not sparse, we can return the COM slot directly
if (!pNewMT->IsSparseForCOMInterop())
{
@@ -6722,9 +6713,18 @@ WORD GetEquivalentMethodSlot(MethodTable * pOldMT, MethodTable * pNewMT, WORD wM
_ASSERTE(!*pfFound);
return 0;
+
+#else
+ // No COM means there is no sparse interface
+ if (wVTslot < pNewMT->GetNumVirtuals())
+ *pfFound = TRUE;
+
+ return wVTslot;
+
+#endif // FEATURE_COMINTEROP
}
#endif // #ifdef DACCESS_COMPILE
-#endif // #ifdef FEATURE_COMINTEROP
+#endif // #ifdef FEATURE_TYPEEQUIVALENCE
//==========================================================================================
BOOL
diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h
index b0dc5e7575..72482f29fe 100644
--- a/src/vm/methodtable.h
+++ b/src/vm/methodtable.h
@@ -746,11 +746,6 @@ public:
BOOL IsExtensibleRCW();
-#if defined(FEATURE_TYPEEQUIVALENCE)
- // mark the type as opted into type equivalence
- void SetHasTypeEquivalence();
-#endif
-
// Helper to get parent class skipping over COM class in
// the hierarchy
MethodTable* GetComPlusParentMethodTable();
@@ -822,19 +817,24 @@ public:
BOOL IsICastable(); // This type implements ICastable interface
#ifdef FEATURE_TYPEEQUIVALENCE
- // type has opted into type equivalence or is instantiated by/derived from a type that is
- BOOL HasTypeEquivalence()
+ // mark the type as opted into type equivalence
+ void SetHasTypeEquivalence()
{
LIMITED_METHOD_CONTRACT;
- return GetFlag(enum_flag_HasTypeEquivalence);
+ SetFlag(enum_flag_HasTypeEquivalence);
}
-#else
+#endif // FEATURE_TYPEEQUIVALENCE
+
+ // type has opted into type equivalence or is instantiated by/derived from a type that is
BOOL HasTypeEquivalence()
{
LIMITED_METHOD_CONTRACT;
+#ifdef FEATURE_TYPEEQUIVALENCE
+ return GetFlag(enum_flag_HasTypeEquivalence);
+#else
return FALSE;
+#endif // FEATURE_TYPEEQUIVALENCE
}
-#endif
//-------------------------------------------------------------------
// DYNAMIC ADDITION OF INTERFACES FOR COM INTEROP
@@ -2019,12 +2019,12 @@ public:
#ifndef DACCESS_COMPILE
FORCEINLINE BOOL IsEquivalentTo(MethodTable *pOtherMT COMMA_INDEBUG(TypeHandlePairList *pVisited = NULL));
-#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_TYPEEQUIVALENCE
// This method is public so that TypeHandle has direct access to it
BOOL IsEquivalentTo_Worker(MethodTable *pOtherMT COMMA_INDEBUG(TypeHandlePairList *pVisited)); // out-of-line part, SO tolerant
private:
BOOL IsEquivalentTo_WorkerInner(MethodTable *pOtherMT COMMA_INDEBUG(TypeHandlePairList *pVisited)); // out-of-line part, SO intolerant
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_TYPEEQUIVALENCE
#endif
public:
@@ -4168,9 +4168,9 @@ public:
#ifndef CROSSBITNESS_COMPILE
static_assert_no_msg(sizeof(MethodTable) == SIZEOF__MethodTable_);
#endif
-#if defined(FEATURE_COMINTEROP) && !defined(DACCESS_COMPILE)
+#if defined(FEATURE_TYPEEQUIVALENCE) && !defined(DACCESS_COMPILE)
WORD GetEquivalentMethodSlot(MethodTable * pOldMT, MethodTable * pNewMT, WORD wMTslot, BOOL *pfFound);
-#endif // defined(FEATURE_COMINTEROP) && !defined(DACCESS_COMPILE)
+#endif // defined(FEATURE_TYPEEQUIVALENCE) && !defined(DACCESS_COMPILE)
MethodTable* CreateMinimalMethodTable(Module* pContainingModule,
LoaderHeap* pCreationHeap,
diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
index 105ffb2737..7c52ebcb3c 100644
--- a/src/vm/methodtablebuilder.cpp
+++ b/src/vm/methodtablebuilder.cpp
@@ -11433,7 +11433,12 @@ void MethodTableBuilder::CheckForTypeEquivalence(
if (bmtProp->fIsTypeEquivalent)
{
- BOOL fTypeEquivalentNotPermittedDueToType = !(((IsComImport() || bmtProp->fComEventItfType) && IsInterface()) || IsValueClass() || IsDelegate());
+ BOOL comImportOrEventInterface = IsComImport();
+#ifdef FEATURE_COMINTEROP
+ comImportOrEventInterface = comImportOrEventInterface || bmtProp->fComEventItfType;
+#endif // FEATURE_COMINTEROP
+
+ BOOL fTypeEquivalentNotPermittedDueToType = !((comImportOrEventInterface && IsInterface()) || IsValueClass() || IsDelegate());
BOOL fTypeEquivalentNotPermittedDueToGenerics = bmtGenerics->HasInstantiation();
if (fTypeEquivalentNotPermittedDueToType || fTypeEquivalentNotPermittedDueToGenerics)
diff --git a/src/vm/siginfo.cpp b/src/vm/siginfo.cpp
index 1995d51515..0a9599edc9 100644
--- a/src/vm/siginfo.cpp
+++ b/src/vm/siginfo.cpp
@@ -2795,7 +2795,11 @@ HRESULT TypeIdentifierData::Init(Module *pModule, mdToken tk)
else
{
// no TypeIdentifierAttribute -> the assembly must be a type library
- bool has_eq = !pModule->GetAssembly()->IsDynamic() && pModule->GetAssembly()->IsPIAOrImportedFromTypeLib();
+ bool has_eq = !pModule->GetAssembly()->IsDynamic();
+
+#ifdef FEATURE_COMINTEROP
+ has_eq = has_eq && pModule->GetAssembly()->IsPIAOrImportedFromTypeLib();
+#endif // FEATURE_COMINTEROP
if (!has_eq)
{
@@ -2900,33 +2904,6 @@ BOOL TypeIdentifierData::IsEqual(const TypeIdentifierData & data) const
(memcmp(m_pchIdentifierName, data.m_pchIdentifierName + m_cbIdentifierNamespace + 1, m_cbIdentifierName) == 0);
}
-#endif //FEATURE_TYPEEQUIVALENCE
-#ifdef FEATURE_COMINTEROP
-
-//---------------------------------------------------------------------------------------
-//
-static CorElementType GetFieldSigElementType(PCCOR_SIGNATURE pSig, DWORD cbSig)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END
-
- SigPointer sigptr(pSig, cbSig);
-
- ULONG data;
- IfFailThrow(sigptr.GetCallingConv(&data));
- _ASSERTE(data == IMAGE_CEE_CS_CALLCONV_FIELD);
-
- CorElementType etype;
- IfFailThrow(sigptr.GetElemType(&etype));
-
- return etype;
-}
-
//---------------------------------------------------------------------------------------
//
static BOOL CompareStructuresForEquivalence(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2, BOOL fEnumMode, TokenPairList *pVisited)
@@ -3073,7 +3050,7 @@ static BOOL CompareDelegatesForEquivalence(mdToken tk1, mdToken tk2, Module *pMo
return MetaSig::CompareMethodSigs(pSig1, cbSig1, pModule1, NULL, pSig2, cbSig2, pModule2, NULL, pVisited);
}
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_TYPEEQUIVALENCE
#endif // #ifndef DACCESS_COMPILE
#ifndef DACCESS_COMPILE
@@ -3178,7 +3155,12 @@ BOOL IsTypeDefEquivalent(mdToken tk, Module *pModule)
// 1. Type is within assembly marked with ImportedFromTypeLibAttribute or PrimaryInteropAssemblyAttribute
if (hr != S_OK)
{
- bool has_eq = !pModule->GetAssembly()->IsDynamic() && pModule->GetAssembly()->IsPIAOrImportedFromTypeLib();
+ // no TypeIdentifierAttribute -> the assembly must be a type library
+ bool has_eq = !pModule->GetAssembly()->IsDynamic();
+
+#ifdef FEATURE_COMINTEROP
+ has_eq = has_eq && pModule->GetAssembly()->IsPIAOrImportedFromTypeLib();
+#endif // FEATURE_COMINTEROP
if (!has_eq)
return FALSE;
@@ -3426,7 +3408,7 @@ BOOL CompareTypeDefsForEquivalence(mdToken tk1, mdToken tk2, Module *pModule1, M
}
return TRUE;
-#else //!defined(DACCESS_COMPILE) && defined(FEATURE_COMINTEROP)
+#else //!defined(DACCESS_COMPILE) && defined(FEATURE_TYPEEQUIVALENCE)
#ifdef DACCESS_COMPILE
// We shouldn't execute this code in dac builds.
diff --git a/src/vm/typeequivalencehash.cpp b/src/vm/typeequivalencehash.cpp
index ca2ecfdb2d..90b2c2749e 100644
--- a/src/vm/typeequivalencehash.cpp
+++ b/src/vm/typeequivalencehash.cpp
@@ -55,8 +55,8 @@ TypeEquivalenceHashTable *TypeEquivalenceHashTable::Create(AppDomain *pAppDomain
LoaderHeap *pHeap = pAppDomain->GetLowFrequencyHeap();
TypeEquivalenceHashTable *pThis = (TypeEquivalenceHashTable*)amt.Track(pHeap->AllocMem((S_SIZE_T)sizeof(TypeEquivalenceHashTable)));
- // The base class get initialized through chaining of constructors. We allocated the hash instance via the
- // loader heap instead of new so use an in-place new to call the constructors now.
+ // The base class gets initialized through chaining of constructors.
+ // Use in-place new to create instance.
new (pThis) TypeEquivalenceHashTable(pHeap, dwNumBuckets, pCrst);
amt.SuppressRelease();
diff --git a/src/vm/typeequivalencehash.hpp b/src/vm/typeequivalencehash.hpp
index 3ce5231ac9..d750ff0659 100644
--- a/src/vm/typeequivalencehash.hpp
+++ b/src/vm/typeequivalencehash.hpp
@@ -10,13 +10,13 @@
#ifndef __TYPEEQUIVALENCE_HASH_INCLUDED
#define __TYPEEQUIVALENCE_HASH_INCLUDED
-#include "ngenhash.h"
-
#ifdef FEATURE_TYPEEQUIVALENCE
+#include "ngenhash.h"
+
// The type of each entry in the hash.
typedef DPTR(struct TypeEquivalenceEntry) PTR_TypeEquivalenceEntry;
-typedef struct TypeEquivalenceEntry
+struct TypeEquivalenceEntry
{
static NgenHashValue HashTypeHandles(TypeHandle thA, TypeHandle thB)
{
@@ -33,8 +33,8 @@ typedef struct TypeEquivalenceEntry
{
LIMITED_METHOD_CONTRACT;
- return (((thA == m_thA) && (thB == m_thB)) ||
- ((thB == m_thA) && (thA == m_thB)));
+ return (((thA == m_thA) && (thB == m_thB))
+ || ((thB == m_thA) && (thA == m_thB)));
}
void SetData(TypeHandle thA, TypeHandle thB, bool fEquivalent)
@@ -53,11 +53,10 @@ typedef struct TypeEquivalenceEntry
}
private:
-
TypeHandle m_thA;
TypeHandle m_thB;
bool m_fEquivalent;
-} TypeEquivalenceEntry_t;
+};
// The hash type itself. All common logic is provided by the NgenHashTable templated base class. See
// NgenHash.h for details.
@@ -70,7 +69,7 @@ class TypeEquivalenceHashTable : public NgenHashTable<TypeEquivalenceHashTable,
#endif
public:
- typedef enum EquivalenceMatch
+ enum EquivalenceMatch
{
MatchUnknown,
Match,
@@ -88,29 +87,29 @@ public:
EquivalenceMatch CheckEquivalence(TypeHandle thA, TypeHandle thB);
#ifdef DACCESS_COMPILE
- void EnumMemoryRegionsForEntry(TypeEquivalenceEntry_t *pEntry, CLRDataEnumMemoryFlags flags) { return; }
+ void EnumMemoryRegionsForEntry(TypeEquivalenceEntry *pEntry, CLRDataEnumMemoryFlags flags) { return; }
#endif
#if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE)
private:
-
- bool ShouldSave(DataImage *pImage, TypeEquivalenceEntry_t *pEntry) { return false; }
- bool IsHotEntry(TypeEquivalenceEntry_t *pEntry, CorProfileData *pProfileData) { return false; }
- bool SaveEntry(DataImage *pImage, CorProfileData *pProfileData, TypeEquivalenceEntry_t *pOldEntry, TypeEquivalenceEntry_t *pNewEntry, EntryMappingTable *pMap) { return true; }
- void FixupEntry(DataImage *pImage, TypeEquivalenceEntry_t *pEntry, void *pFixupBase, DWORD cbFixupOffset) { return; }
+ // Override operations from NgenHashTable - see ngenhash.h
+ bool ShouldSave(DataImage *pImage, TypeEquivalenceEntry *pEntry) { return false; }
+ bool IsHotEntry(TypeEquivalenceEntry *pEntry, CorProfileData *pProfileData) { return false; }
+ bool SaveEntry(DataImage *pImage, CorProfileData *pProfileData, TypeEquivalenceEntry *pOldEntry, TypeEquivalenceEntry *pNewEntry, EntryMappingTable *pMap) { return true; }
+ void FixupEntry(DataImage *pImage, TypeEquivalenceEntry *pEntry, void *pFixupBase, DWORD cbFixupOffset) { return; }
#endif // FEATURE_PREJIT && !DACCESS_COMPILE
private:
#ifndef DACCESS_COMPILE
- TypeEquivalenceHashTable(LoaderHeap *pHeap, DWORD cInitialBuckets, CrstExplicitInit *pCrst) :
- NgenHashTable<TypeEquivalenceHashTable, TypeEquivalenceEntry, 4>(NULL, pHeap, cInitialBuckets),
- m_pHashTableCrst(pCrst)
+ TypeEquivalenceHashTable(LoaderHeap *pHeap, DWORD cInitialBuckets, CrstExplicitInit *pCrst)
+ : NgenHashTable<TypeEquivalenceHashTable, TypeEquivalenceEntry, 4>(NULL, pHeap, cInitialBuckets)
+ , m_pHashTableCrst(pCrst)
{
}
-#endif
- CrstExplicitInit* m_pHashTableCrst;
+#endif // DACCESS_COMPILE
+
+ CrstExplicitInit* m_pHashTableCrst;
};
#endif // FEATURE_TYPEEQUIVALENCE
-
-#endif // !__CLASS_HASH_INCLUDED
+#endif // !__TYPEEQUIVALENCE_HASH_INCLUDED
diff --git a/tests/src/baseservices/typeequivalence/TypeEquivalence.props b/tests/src/baseservices/typeequivalence/TypeEquivalence.props
new file mode 100644
index 0000000000..e53ed81469
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/TypeEquivalence.props
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <!-- The Type Equivalence feature is unsupported outside of windows -->
+ <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+ <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+ </PropertyGroup>
+
+</Project> \ No newline at end of file
diff --git a/tests/src/baseservices/typeequivalence/TypeEquivalence.targets b/tests/src/baseservices/typeequivalence/TypeEquivalence.targets
new file mode 100644
index 0000000000..5cc9a07c52
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/TypeEquivalence.targets
@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <Target
+ Name="CreateEmbedResponseFile"
+ AfterTargets="ResolveProjectReferences">
+
+ <!-- Get the output for each project reference -->
+ <Message Text="Generating Response File for embedding type metadata: $(CompilerResponseFile)"/>
+ <MSBuild
+ Projects="@(ProjectReference)"
+ Condition=" '%(ProjectReference.EmbedTypes)' == 'true'"
+ Targets="GetTargetPath">
+ <Output TaskParameter="TargetOutputs" ItemName="ResolvedProjectReferencePaths" />
+ </MSBuild>
+
+ <WriteLinesToFile
+ File="$(CompilerResponseFile)"
+ Lines="@(ResolvedProjectReferencePaths -> '/link:%(fullpath)')"
+ Overwrite="true"
+ Encoding="Unicode" />
+ </Target>
+
+</Project> \ No newline at end of file
diff --git a/tests/src/baseservices/typeequivalence/contracts/TypeContracts.csproj b/tests/src/baseservices/typeequivalence/contracts/TypeContracts.csproj
new file mode 100644
index 0000000000..17df1f5587
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/contracts/TypeContracts.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$([MSBuild]::GetPathOfFileAbove('TypeEquivalence.props', '$(MSBuildThisFileDirectory)../'))" />
+
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="**/*.cs" />
+ </ItemGroup>
+
+</Project> \ No newline at end of file
diff --git a/tests/src/baseservices/typeequivalence/contracts/Types.cs b/tests/src/baseservices/typeequivalence/contracts/Types.cs
new file mode 100644
index 0000000000..5163a6f249
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/contracts/Types.cs
@@ -0,0 +1,18 @@
+// 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.
+
+using System;
+using System.Runtime.InteropServices;
+
+[assembly:ImportedFromTypeLib("TypeEquivalenceTest")] // Required to support embeddable types
+[assembly:Guid("3B491C47-B176-4CF3-8748-F19E303F1714")]
+
+namespace TypeEquivalenceTypes
+{
+ [ComImport]
+ [Guid("F34D4DE8-B891-4D73-B177-C8F1139A9A67")]
+ public interface IEmptyType
+ {
+ }
+}
diff --git a/tests/src/baseservices/typeequivalence/impl/Impls.cs b/tests/src/baseservices/typeequivalence/impl/Impls.cs
new file mode 100644
index 0000000000..e9129fe1ed
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/impl/Impls.cs
@@ -0,0 +1,19 @@
+// 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.
+
+using System;
+using System.Runtime.InteropServices;
+
+using TypeEquivalenceTypes;
+
+public class EmptyType : IEmptyType
+{
+ /// <summary>
+ /// Create an instance of <see cref="EmptyType" />
+ /// </summary>
+ public static object Create()
+ {
+ return new EmptyType();
+ }
+} \ No newline at end of file
diff --git a/tests/src/baseservices/typeequivalence/impl/TypeImpl.csproj b/tests/src/baseservices/typeequivalence/impl/TypeImpl.csproj
new file mode 100644
index 0000000000..665f26ca9c
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/impl/TypeImpl.csproj
@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$([MSBuild]::GetPathOfFileAbove('TypeEquivalence.props', '$(MSBuildThisFileDirectory)../'))" />
+
+ <PropertyGroup>
+ <OutputType>Library</OutputType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="**/*.cs" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <CompilerResponseFile>$(IntermediateOutputPath)TypeImpl.rsp</CompilerResponseFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="../contracts/TypeContracts.csproj">
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ <EmbedTypes>true</EmbedTypes>
+ </ProjectReference>
+ </ItemGroup>
+
+ <Import Project="$([MSBuild]::GetPathOfFileAbove('TypeEquivalence.targets', '$(MSBuildThisFileDirectory)../'))" />
+
+</Project> \ No newline at end of file
diff --git a/tests/src/baseservices/typeequivalence/simple/Simple.cs b/tests/src/baseservices/typeequivalence/simple/Simple.cs
new file mode 100644
index 0000000000..dd42ddd837
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/simple/Simple.cs
@@ -0,0 +1,53 @@
+// 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.
+
+using System;
+using System.Runtime.InteropServices;
+
+using TestLibrary;
+using TypeEquivalenceTypes;
+
+public class Simple
+{
+ private class EmptyType2 : IEmptyType
+ {
+ /// <summary>
+ /// Create an instance of <see cref="EmptyType" />
+ /// </summary>
+ public static object Create()
+ {
+ return new EmptyType2();
+ }
+ }
+
+ private static void InterfaceTypesFromDifferentAssembliesAreEqual()
+ {
+ Console.WriteLine("Interfaces are the same");
+ var inAsm = EmptyType.Create();
+ DisplayType((IEmptyType)inAsm);
+
+ var otherAsm = EmptyType2.Create();
+ DisplayType((IEmptyType)otherAsm);
+
+ void DisplayType(IEmptyType i)
+ {
+ Console.WriteLine(i.GetType());
+ }
+ }
+
+ public static int Main(string[] noArgs)
+ {
+ try
+ {
+ InterfaceTypesFromDifferentAssembliesAreEqual();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+
+ return 100;
+ }
+} \ No newline at end of file
diff --git a/tests/src/baseservices/typeequivalence/simple/Simple.csproj b/tests/src/baseservices/typeequivalence/simple/Simple.csproj
new file mode 100644
index 0000000000..e9cd889ca3
--- /dev/null
+++ b/tests/src/baseservices/typeequivalence/simple/Simple.csproj
@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$([MSBuild]::GetPathOfFileAbove('TypeEquivalence.props', '$(MSBuildThisFileDirectory)../'))" />
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="**/*.cs" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <CompilerResponseFile>$(IntermediateOutputPath)Simple.rsp</CompilerResponseFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="../contracts/TypeContracts.csproj">
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ <EmbedTypes>true</EmbedTypes>
+ </ProjectReference>
+ <ProjectReference Include="../impl/TypeImpl.csproj"/>
+ <ProjectReference Include="../../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+ </ItemGroup>
+
+ <Import Project="$([MSBuild]::GetPathOfFileAbove('TypeEquivalence.targets', '$(MSBuildThisFileDirectory)../'))" />
+
+</Project> \ No newline at end of file