static BOOL tiCompatibleWithByRef(COMP_HANDLE CompHnd, const typeInfo& child, const typeInfo& parent) { assert(parent.IsByRef()); if (!child.IsByRef()) { return FALSE; } if (child.IsReadonlyByRef() && !parent.IsReadonlyByRef()) { return FALSE; } // Byrefs are compatible if the underlying types are equivalent typeInfo childTarget = ::DereferenceByRef(child); typeInfo parentTarget = ::DereferenceByRef(parent); if (typeInfo::AreEquivalent(childTarget, parentTarget)) { return TRUE; } // Make sure that both types have a valid m_cls if ((childTarget.IsType(TI_REF) || childTarget.IsType(TI_STRUCT)) && (parentTarget.IsType(TI_REF) || parentTarget.IsType(TI_STRUCT))) { return CompHnd->areTypesEquivalent(childTarget.GetClassHandle(), parentTarget.GetClassHandle()); } return FALSE; }
BOOL typeInfo::tiMergeToCommonParent(COMP_HANDLE CompHnd, typeInfo* pDest, const typeInfo* pSrc, bool* changed) { assert(pSrc->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pSrc), *pSrc)); assert(pDest->IsDead() || typeInfo::AreEquivalent(::NormaliseForStack(*pDest), *pDest)); // Merge the auxiliary information like "this" pointer tracking, etc... // Remember the pre-state, so we can tell if it changed. *changed = false; DWORD destFlagsBefore = pDest->m_flags; // This bit is only set if both pDest and pSrc have it set pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_THIS_PTR); // This bit is set if either pDest or pSrc have it set pDest->m_flags |= (pSrc->m_flags & TI_FLAG_UNINIT_OBJREF); // This bit is set if either pDest or pSrc have it set pDest->m_flags |= (pSrc->m_flags & TI_FLAG_BYREF_READONLY); // If the byref wasn't permanent home in both sides, then merge won't have the bit set pDest->m_flags &= (pSrc->m_flags | ~TI_FLAG_BYREF_PERMANENT_HOME); if (pDest->m_flags != destFlagsBefore) { *changed = true; } // OK the main event. Merge the main types if (typeInfo::AreEquivalent(*pDest, *pSrc)) { return (TRUE); } if (pDest->IsUnboxedGenericTypeVar() || pSrc->IsUnboxedGenericTypeVar()) { // Should have had *pDest == *pSrc goto FAIL; } if (pDest->IsType(TI_REF)) { if (pSrc->IsType(TI_NULL)) { // NULL can be any reference type return TRUE; } if (!pSrc->IsType(TI_REF)) { goto FAIL; } // Ask the EE to find the common parent, This always succeeds since System.Object always works CORINFO_CLASS_HANDLE pDestClsBefore = pDest->m_cls; pDest->m_cls = CompHnd->mergeClasses(pDest->GetClassHandle(), pSrc->GetClassHandle()); if (pDestClsBefore != pDest->m_cls) { *changed = true; } return TRUE; } else if (pDest->IsType(TI_NULL)) { if (pSrc->IsType(TI_REF)) // NULL can be any reference type { *pDest = *pSrc; *changed = true; return TRUE; } goto FAIL; } else if (pDest->IsType(TI_STRUCT)) { if (pSrc->IsType(TI_STRUCT) && CompHnd->areTypesEquivalent(pDest->GetClassHandle(), pSrc->GetClassHandle())) { return TRUE; } goto FAIL; } else if (pDest->IsByRef()) { return tiCompatibleWithByRef(CompHnd, *pSrc, *pDest); } #ifdef _TARGET_64BIT_ // On 64-bit targets we have precise representation for native int, so these rules // represent the fact that the ECMA spec permits the implicit conversion // between an int32 and a native int. else if (typeInfo::AreEquivalent(*pDest, typeInfo::nativeInt()) && pSrc->IsType(TI_INT)) { return TRUE; } else if (typeInfo::AreEquivalent(*pSrc, typeInfo::nativeInt()) && pDest->IsType(TI_INT)) { *pDest = *pSrc; *changed = true; return TRUE; } #endif // _TARGET_64BIT_ FAIL: *pDest = typeInfo(); return FALSE; }
BOOL typeInfo::tiCompatibleWith(COMP_HANDLE CompHnd, const typeInfo& child, const typeInfo& parent, bool normalisedForStack) { assert(child.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(child), child)); assert(parent.IsDead() || !normalisedForStack || typeInfo::AreEquivalent(::NormaliseForStack(parent), parent)); if (typeInfo::AreEquivalent(child, parent)) { return TRUE; } if (parent.IsUnboxedGenericTypeVar() || child.IsUnboxedGenericTypeVar()) { return (FALSE); // need to have had child == parent } else if (parent.IsType(TI_REF)) { // An uninitialized objRef is not compatible to initialized. if (child.IsUninitialisedObjRef() && !parent.IsUninitialisedObjRef()) { return FALSE; } if (child.IsNullObjRef()) { // NULL can be any reference type return TRUE; } if (!child.IsType(TI_REF)) { return FALSE; } return CompHnd->canCast(child.m_cls, parent.m_cls); } else if (parent.IsType(TI_METHOD)) { if (!child.IsType(TI_METHOD)) { return FALSE; } // Right now we don't bother merging method handles return FALSE; } else if (parent.IsType(TI_STRUCT)) { if (!child.IsType(TI_STRUCT)) { return FALSE; } // Structures are compatible if they are equivalent return CompHnd->areTypesEquivalent(child.m_cls, parent.m_cls); } else if (parent.IsByRef()) { return tiCompatibleWithByRef(CompHnd, child, parent); } #ifdef _TARGET_64BIT_ // On 64-bit targets we have precise representation for native int, so these rules // represent the fact that the ECMA spec permits the implicit conversion // between an int32 and a native int. else if (parent.IsType(TI_INT) && typeInfo::AreEquivalent(nativeInt(), child)) { return TRUE; } else if (typeInfo::AreEquivalent(nativeInt(), parent) && child.IsType(TI_INT)) { return TRUE; } #endif // _TARGET_64BIT_ return FALSE; }