Enable type equivalence (dotnet/coreclr#21265)
authorAaron Robinson <arobins@microsoft.com>
Thu, 29 Nov 2018 20:44:04 +0000 (12:44 -0800)
committerGitHub <noreply@github.com>
Thu, 29 Nov 2018 20:44:04 +0000 (12:44 -0800)
* Enable TypeEquivalence feature for Windows platform

* Basic test - verified test exercises TypeEquivalence code paths

Commit migrated from https://github.com/dotnet/coreclr/commit/11d1b645f0dede73ded0030b56d7c506150b0741

24 files changed:
src/coreclr/clr.coreclr.props
src/coreclr/clr.defines.targets
src/coreclr/clrdefinitions.cmake
src/coreclr/src/vm/CMakeLists.txt
src/coreclr/src/vm/appdomain.cpp
src/coreclr/src/vm/class.h
src/coreclr/src/vm/crossgen/CMakeLists.txt
src/coreclr/src/vm/method.cpp
src/coreclr/src/vm/method.hpp
src/coreclr/src/vm/method.inl
src/coreclr/src/vm/methodtable.cpp
src/coreclr/src/vm/methodtable.h
src/coreclr/src/vm/methodtablebuilder.cpp
src/coreclr/src/vm/siginfo.cpp
src/coreclr/src/vm/typeequivalencehash.cpp
src/coreclr/src/vm/typeequivalencehash.hpp
src/coreclr/tests/src/baseservices/typeequivalence/TypeEquivalence.props [new file with mode: 0644]
src/coreclr/tests/src/baseservices/typeequivalence/TypeEquivalence.targets [new file with mode: 0644]
src/coreclr/tests/src/baseservices/typeequivalence/contracts/TypeContracts.csproj [new file with mode: 0644]
src/coreclr/tests/src/baseservices/typeequivalence/contracts/Types.cs [new file with mode: 0644]
src/coreclr/tests/src/baseservices/typeequivalence/impl/Impls.cs [new file with mode: 0644]
src/coreclr/tests/src/baseservices/typeequivalence/impl/TypeImpl.csproj [new file with mode: 0644]
src/coreclr/tests/src/baseservices/typeequivalence/simple/Simple.cs [new file with mode: 0644]
src/coreclr/tests/src/baseservices/typeequivalence/simple/Simple.csproj [new file with mode: 0644]

index 872cbbc..6ebc360 100644 (file)
@@ -7,6 +7,7 @@
     <FeatureManagedEtwChannels>true</FeatureManagedEtwChannels>
     <FeatureManagedEtw>true</FeatureManagedEtw>
     <FeaturePerfTracing>true</FeaturePerfTracing>
+    <FeatureTypeEquivalence>true</FeatureTypeEquivalence>
     <ProfilingSupportedBuild>true</ProfilingSupportedBuild>
   </PropertyGroup>
 
index 32876ea..b440cdd 100644 (file)
@@ -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>
index 2007f0d..11d2a43 100644 (file)
@@ -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)
index 06532b3..c5873ee 100644 (file)
@@ -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
index 0a5e9de..b0cb03b 100644 (file)
@@ -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;
index 34aac07..54253ea 100644 (file)
@@ -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
index 1d1eb23..8c9bfc5 100644 (file)
@@ -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
index 3c2ba7d..9c19c68 100644 (file)
@@ -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()
 {
index 529064d..8b136e6 100644 (file)
@@ -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()
     {
index ca9a17a..9d55ae9 100644 (file)
@@ -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()
 {
index d83e62d..5ed91d1 100644 (file)
@@ -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 
index b0dc5e7..72482f2 100644 (file)
@@ -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,
index 105ffb2..7c52ebc 100644 (file)
@@ -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)
index 1995d51..0a9599e 100644 (file)
@@ -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.
index ca2ecfd..90b2c27 100644 (file)
@@ -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();
 
index 3ce5231..d750ff0 100644 (file)
 #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/src/coreclr/tests/src/baseservices/typeequivalence/TypeEquivalence.props b/src/coreclr/tests/src/baseservices/typeequivalence/TypeEquivalence.props
new file mode 100644 (file)
index 0000000..e53ed81
--- /dev/null
@@ -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/src/coreclr/tests/src/baseservices/typeequivalence/TypeEquivalence.targets b/src/coreclr/tests/src/baseservices/typeequivalence/TypeEquivalence.targets
new file mode 100644 (file)
index 0000000..5cc9a07
--- /dev/null
@@ -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/src/coreclr/tests/src/baseservices/typeequivalence/contracts/TypeContracts.csproj b/src/coreclr/tests/src/baseservices/typeequivalence/contracts/TypeContracts.csproj
new file mode 100644 (file)
index 0000000..17df1f5
--- /dev/null
@@ -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/src/coreclr/tests/src/baseservices/typeequivalence/contracts/Types.cs b/src/coreclr/tests/src/baseservices/typeequivalence/contracts/Types.cs
new file mode 100644 (file)
index 0000000..5163a6f
--- /dev/null
@@ -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/src/coreclr/tests/src/baseservices/typeequivalence/impl/Impls.cs b/src/coreclr/tests/src/baseservices/typeequivalence/impl/Impls.cs
new file mode 100644 (file)
index 0000000..e9129fe
--- /dev/null
@@ -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/src/coreclr/tests/src/baseservices/typeequivalence/impl/TypeImpl.csproj b/src/coreclr/tests/src/baseservices/typeequivalence/impl/TypeImpl.csproj
new file mode 100644 (file)
index 0000000..665f26c
--- /dev/null
@@ -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/src/coreclr/tests/src/baseservices/typeequivalence/simple/Simple.cs b/src/coreclr/tests/src/baseservices/typeequivalence/simple/Simple.cs
new file mode 100644 (file)
index 0000000..dd42ddd
--- /dev/null
@@ -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/src/coreclr/tests/src/baseservices/typeequivalence/simple/Simple.csproj b/src/coreclr/tests/src/baseservices/typeequivalence/simple/Simple.csproj
new file mode 100644 (file)
index 0000000..e9cd889
--- /dev/null
@@ -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