diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 479bf176691485..07d1acd6a4a6c4 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -8394,7 +8394,6 @@ MethodTableBuilder::HandleExplicitLayout( // Instance slice size is the total size of an instance, and is calculated as // the field whose offset and size add to the greatest number. UINT instanceSliceSize = 0; - DWORD firstObjectOverlapOffset = ((DWORD)(-1)); UINT i; for (i = 0; i < bmtMetaData->cFields; i++) @@ -8433,15 +8432,19 @@ MethodTableBuilder::HandleExplicitLayout( // // 1. Verify that every OREF or BYREF is on a valid alignment. // 2. Verify that OREFs only overlap with other OREFs. - // 3. If an OREF does overlap with another OREF, the class is marked unverifiable. - // 4. If an overlap of any kind occurs, the class will be marked NotTightlyPacked (affects ValueType.Equals()). + // 3. Verify that BYREFs only overlap with other BYREFs. + // 4. If an OREF does overlap with another OREF, the class is marked unverifiable. + // 5. If a BYREF does overlap with another BYREF, the class is marked unverifiable. + // 6. If an overlap of any kind occurs, the class will be marked NotTightlyPacked (affects ValueType.Equals()). // char emptyObject[TARGET_POINTER_SIZE]; char isObject[TARGET_POINTER_SIZE]; + char isByRef[TARGET_POINTER_SIZE]; for (i = 0; i < TARGET_POINTER_SIZE; i++) { emptyObject[i] = empty; isObject[i] = oref; + isByRef[i] = byref; } ExplicitClassTrust explicitClassTrust; @@ -8482,7 +8485,7 @@ MethodTableBuilder::HandleExplicitLayout( CorElementType type = pFD->GetFieldType(); if (CorTypeInfo::IsObjRef(type) || CorTypeInfo::IsByRef(type)) { - // Check that the ref offset is pointer aligned + // Check that the field is pointer aligned if ((pFD->GetOffset_NoLogging() & ((ULONG)TARGET_POINTER_SIZE - 1)) != 0) { badOffset = pFD->GetOffset_NoLogging(); @@ -8491,33 +8494,43 @@ MethodTableBuilder::HandleExplicitLayout( // If we got here, OREF or BYREF field was not pointer aligned. THROW. break; } - } - if (CorTypeInfo::IsObjRef(type)) - { - // check if overlaps another object - if (memcmp((void *)&pFieldLayout[pFD->GetOffset_NoLogging()], (void *)isObject, sizeof(isObject)) == 0) + // Determine which tag type we are working with. + bmtFieldLayoutTag tag; + SIZE_T tagBlockSize; + void* tagBlock; + if (CorTypeInfo::IsObjRef(type)) { - // If we got here, an OREF overlapped another OREF. We permit this but mark the class unverifiable. - fieldTrust.SetTrust(ExplicitFieldTrust::kLegal); - - if (firstObjectOverlapOffset == ((DWORD)(-1))) - { - firstObjectOverlapOffset = pFD->GetOffset_NoLogging(); - } + tagBlockSize = sizeof(isObject); + tagBlock = (void*)isObject; + tag = oref; + } + else + { + _ASSERTE(CorTypeInfo::IsByRef(type)); + tagBlockSize = sizeof(isByRef); + tagBlock = (void*)isByRef; + tag = byref; + } + // Check if there is overlap with its own tag type + if (memcmp((void *)&pFieldLayout[pFD->GetOffset_NoLogging()], tagBlock, tagBlockSize) == 0) + { + // If we got here, there is tag type overlap. We permit this but mark the class unverifiable. + fieldTrust.SetTrust(ExplicitFieldTrust::kLegal); continue; } - // check if is empty at this point + // check if typed layout is empty at this point if (memcmp((void *)&pFieldLayout[pFD->GetOffset_NoLogging()], (void *)emptyObject, sizeof(emptyObject)) == 0) { - // If we got here, this OREF is overlapping no other fields (yet). Record that these bytes now contain an OREF. - memset((void *)&pFieldLayout[pFD->GetOffset_NoLogging()], oref, sizeof(isObject)); + // If we got here, this tag type is overlapping no other fields (yet). + // Record that these bytes now contain the current tag type. + memset((void *)&pFieldLayout[pFD->GetOffset_NoLogging()], tag, tagBlockSize); fieldTrust.SetTrust(ExplicitFieldTrust::kNonOverLayed); continue; } - // If we got here, the OREF overlaps a non-OREF. THROW. + // If we got here, the tag overlaps something else. THROW. badOffset = pFD->GetOffset_NoLogging(); fieldTrust.SetTrust(ExplicitFieldTrust::kNone); break; @@ -8525,64 +8538,57 @@ MethodTableBuilder::HandleExplicitLayout( else { UINT fieldSize; - if (pFD->IsByValue()) + if (!pFD->IsByValue()) + { + fieldSize = GetFieldSize(pFD); + } + else { MethodTable *pByValueMT = pByValueClassCache[valueClassCacheIndex]; - if (pByValueMT->IsByRefLike()) + if (pByValueMT->IsByRefLike() || pByValueMT->ContainsPointers()) { if ((pFD->GetOffset_NoLogging() & ((ULONG)TARGET_POINTER_SIZE - 1)) != 0) { - // If we got here, then a byref-like valuetype was misaligned. + // If we got here, then a ByRefLike valuetype or a valuetype containing an OREF was misaligned. badOffset = pFD->GetOffset_NoLogging(); fieldTrust.SetTrust(ExplicitFieldTrust::kNone); break; } - } - if (pByValueMT->ContainsPointers()) - { - if ((pFD->GetOffset_NoLogging() & ((ULONG)TARGET_POINTER_SIZE - 1)) == 0) - { - ExplicitFieldTrust::TrustLevel trust; - DWORD firstObjectOverlapOffsetInsideValueClass = ((DWORD)(-1)); - trust = CheckValueClassLayout(pByValueMT, &pFieldLayout[pFD->GetOffset_NoLogging()], &firstObjectOverlapOffsetInsideValueClass); - fieldTrust.SetTrust(trust); - if (firstObjectOverlapOffsetInsideValueClass != ((DWORD)(-1))) - { - if (firstObjectOverlapOffset == ((DWORD)(-1))) - { - firstObjectOverlapOffset = pFD->GetOffset_NoLogging() + firstObjectOverlapOffsetInsideValueClass; - } - } - if (trust != ExplicitFieldTrust::kNone) - { - continue; - } - else - { - // If we got here, then an OREF inside the valuetype illegally overlapped a non-OREF field. THROW. - badOffset = pFD->GetOffset_NoLogging(); - break; - } + ExplicitFieldTrust::TrustLevel trust = CheckValueClassLayout(pByValueMT, &pFieldLayout[pFD->GetOffset_NoLogging()]); + fieldTrust.SetTrust(trust); + + if (trust != ExplicitFieldTrust::kNone) + { + continue; + } + else + { + // If we got here, then an OREF/BYREF inside the valuetype illegally overlapped a non-OREF field. THROW. + badOffset = pFD->GetOffset_NoLogging(); + break; } - // If we got here, then a valuetype containing an OREF was misaligned. - badOffset = pFD->GetOffset_NoLogging(); - fieldTrust.SetTrust(ExplicitFieldTrust::kNone); break; } // no pointers so fall through to do standard checking fieldSize = pByValueMT->GetNumInstanceFieldBytes(); } - else + + // If we got here, we are trying to place a non-OREF (or a valuetype composed of non-OREFs.) + // Look for any orefs or byrefs under this field + BYTE *loc = NULL; + BYTE* currOffset = pFieldLayout + pFD->GetOffset_NoLogging(); + BYTE* endOffset = currOffset + fieldSize; + for (; currOffset < endOffset; ++currOffset) { - // field size temporarily stored in pInterface field - fieldSize = GetFieldSize(pFD); + if (*currOffset == oref || *currOffset == byref) + { + loc = currOffset; + break; + } } - // If we got here, we are trying to place a non-OREF (or a valuetype composed of non-OREFs.) - // Look for any orefs under this field - BYTE *loc; - if ((loc = (BYTE*)memchr((void*)&pFieldLayout[pFD->GetOffset_NoLogging()], oref, fieldSize)) == NULL) + if (loc == NULL) { // If we have a nonoref in the range then we are doing an overlay if(memchr((void*)&pFieldLayout[pFD->GetOffset_NoLogging()], nonoref, fieldSize)) @@ -8598,7 +8604,7 @@ MethodTableBuilder::HandleExplicitLayout( } // If we got here, we tried to place a non-OREF (or a valuetype composed of non-OREFs) - // on top of an OREF. THROW. + // on top of an OREF/BYREF. THROW. badOffset = (UINT)(loc - pFieldLayout); fieldTrust.SetTrust(ExplicitFieldTrust::kNone); break; @@ -8716,27 +8722,29 @@ MethodTableBuilder::HandleExplicitLayout( } // MethodTableBuilder::HandleExplicitLayout //******************************************************************************* -// make sure that no object fields are overlapped incorrectly, returns S_FALSE if -// there overlap but nothing illegal, S_OK if there is no overlap -/*static*/ ExplicitFieldTrust::TrustLevel MethodTableBuilder::CheckValueClassLayout(MethodTable * pMT, BYTE *pFieldLayout, DWORD *pFirstObjectOverlapOffset) +// make sure that no object fields are overlapped incorrectly, returns the trust level +/*static*/ ExplicitFieldTrust::TrustLevel MethodTableBuilder::CheckValueClassLayout(MethodTable * pMT, BYTE *pFieldLayout) { STANDARD_VM_CONTRACT; + // ByRefLike types need to be checked for ByRef fields. + if (pMT->IsByRefLike()) + return CheckByRefLikeValueClassLayout(pMT, pFieldLayout); - *pFirstObjectOverlapOffset = (DWORD)(-1); + // This method assumes there is a GC desc associated with the MethodTable. + _ASSERTE(pMT->ContainsPointers()); - // Build a layout of the value class. Don't know the sizes of all the fields easily, but - // do know a) vc is already consistent so don't need to check it's overlaps and - // b) size and location of all objectrefs. So build it by setting all non-oref + // Build a layout of the value class (vc). Don't know the sizes of all the fields easily, but + // do know (a) vc is already consistent so don't need to check it's overlaps and + // (b) size and location of all objectrefs. So build it by setting all non-oref // then fill in the orefs later UINT fieldSize = pMT->GetNumInstanceFieldBytes(); + CQuickBytes qb; BYTE *vcLayout = (BYTE*) qb.AllocThrows(fieldSize * sizeof(BYTE)); - memset((void*)vcLayout, nonoref, fieldSize); // use pointer series to locate the orefs - CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); CGCDescSeries *pSeries = map->GetLowestSeries(); @@ -8748,11 +8756,9 @@ MethodTableBuilder::HandleExplicitLayout( pSeries++; } - ExplicitClassTrust explicitClassTrust; - - for (UINT i=0; i < fieldSize; i++) { - + for (UINT i=0; i < fieldSize; i++) + { ExplicitFieldTrustHolder fieldTrust(&explicitClassTrust); if (vcLayout[i] == oref) { @@ -8771,10 +8777,6 @@ MethodTableBuilder::HandleExplicitLayout( // oref <--> oref case oref: fieldTrust.SetTrust(ExplicitFieldTrust::kLegal); - if ((*pFirstObjectOverlapOffset) == ((DWORD)(-1))) - { - *pFirstObjectOverlapOffset = (DWORD)i; - } break; default: @@ -8810,10 +8812,92 @@ MethodTableBuilder::HandleExplicitLayout( } +//******************************************************************************* +// make sure that no byref/object fields are overlapped, returns the trust level +/*static*/ ExplicitFieldTrust::TrustLevel MethodTableBuilder::CheckByRefLikeValueClassLayout(MethodTable * pMT, BYTE *pFieldLayout) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(pMT->IsByRefLike()); + + // ByReference is an indication for a byref field, treat it as such. + if (pMT->HasSameTypeDefAs(g_pByReferenceClass)) + return MarkTagType(pFieldLayout, TARGET_POINTER_SIZE, byref); + + ExplicitClassTrust explicitClassTrust; + + ExplicitFieldTrust::TrustLevel trust; + ApproxFieldDescIterator fieldIterator(pMT, ApproxFieldDescIterator::INSTANCE_FIELDS); + for (FieldDesc *pFD = fieldIterator.Next(); pFD != NULL; pFD = fieldIterator.Next()) + { + ExplicitFieldTrustHolder fieldTrust(&explicitClassTrust); + int fieldStartIndex = pFD->GetOffset(); + + if (pFD->GetFieldType() == ELEMENT_TYPE_VALUETYPE) + { + MethodTable *pFieldMT = pFD->GetApproxFieldTypeHandleThrowing().AsMethodTable(); + trust = CheckValueClassLayout(pFieldMT, &pFieldLayout[fieldStartIndex]); + } + else if (pFD->IsObjRef()) + { + _ASSERTE(fieldStartIndex % TARGET_POINTER_SIZE == 0); + trust = MarkTagType(&pFieldLayout[fieldStartIndex], TARGET_POINTER_SIZE, oref); + } + else if (pFD->IsByRef()) + { + _ASSERTE(fieldStartIndex % TARGET_POINTER_SIZE == 0); + trust = MarkTagType(&pFieldLayout[fieldStartIndex], TARGET_POINTER_SIZE, byref); + } + else + { + trust = MarkTagType(&pFieldLayout[fieldStartIndex], pFD->GetSize(), nonoref); + } + + fieldTrust.SetTrust(trust); + + // Some invalid overlap was detected. + if (trust == ExplicitFieldTrust::kNone) + break; + } + + return explicitClassTrust.GetTrustLevel(); +} +//******************************************************************************* +// Set the field's tag type and/or detect invalid overlap +/*static*/ ExplicitFieldTrust::TrustLevel MethodTableBuilder::MarkTagType(BYTE* field, SIZE_T fieldSize, bmtFieldLayoutTag tagType) +{ + STANDARD_VM_CONTRACT; + _ASSERTE(field != NULL); + _ASSERTE(fieldSize != 0); + _ASSERTE(tagType != empty); + ExplicitFieldTrust::TrustLevel trust = ExplicitFieldTrust::kMaxTrust; + for (SIZE_T i = 0; i < fieldSize; ++i) + { + if (field[i] == empty) + { + // Nothing set for overlap, mark as requested. + field[i] = tagType; + } + else if (field[i] == tagType) + { + // Only a overlapped nonoref tag is verifiable, all others are simply legal. + ExplicitFieldTrust::TrustLevel overlapTrust = tagType == nonoref ? ExplicitFieldTrust::kVerifiable : ExplicitFieldTrust::kLegal; + // The ExplicitFieldTrust enum is ranked in descending order of trust. + // We always take the computed minimum trust level. + trust = min(trust, overlapTrust); + } + else + { + // A non-equal overlap was detected. There is no trust for the type. + trust = ExplicitFieldTrust::kNone; + break; + } + } + return trust; +} //******************************************************************************* void MethodTableBuilder::FindPointerSeriesExplicit(UINT instanceSliceSize, diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index 42cae0b22c1e95..7d47c558709aa1 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -2089,7 +2089,7 @@ class MethodTableBuilder // -------------------------------------------------------------------------------------------- // Used for analyzing overlapped fields defined by explicit layout types. - enum bmtFieldLayoutTag {empty, nonoref, oref}; + enum bmtFieldLayoutTag {empty, nonoref, oref, byref}; // -------------------------------------------------------------------------------------------- // used for calculating pointer series for tdexplicit @@ -2932,8 +2932,16 @@ class MethodTableBuilder static ExplicitFieldTrust::TrustLevel CheckValueClassLayout( MethodTable * pMT, - BYTE * pFieldLayout, - DWORD * pFirstObjectOverlapOffset); + BYTE * pFieldLayout); + + static ExplicitFieldTrust::TrustLevel CheckByRefLikeValueClassLayout( + MethodTable * pMT, + BYTE * pFieldLayout); + + static ExplicitFieldTrust::TrustLevel MarkTagType( + BYTE* field, + SIZE_T size, + bmtFieldLayoutTag tagType); void FindPointerSeriesExplicit( UINT instanceSliceSize, diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index 36fdd82960f83b..f0e1fdb2c6bbf9 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -1832,6 +1832,27 @@ type_has_references (MonoClass *klass, MonoType *ftype) return FALSE; } +static gboolean +class_has_ref_fields (MonoClass *klass) +{ + /* + * has_ref_fields is not set if this is called recursively, but this is not a problem since this is only used + * during field layout, and instance fields are initialized before static fields, and instance fields can't + * embed themselves. + */ + return klass->has_ref_fields; +} + +static gboolean +type_has_ref_fields (MonoType *ftype) +{ + if (m_type_is_byref (ftype) || (MONO_TYPE_ISSTRUCT (ftype) && class_has_ref_fields (mono_class_from_mono_type_internal (ftype)))) + return TRUE; + + return FALSE; +} + + /** * mono_class_is_gparam_with_nonblittable_parent: * \param klass a generic parameter @@ -1941,7 +1962,7 @@ validate_struct_fields_overlaps (guint8 *layout_check, int layout_size, MonoClas } else { int align = 0; int size = mono_type_size (field->type, &align); - guint8 type = type_has_references (klass, ftype) ? 1 : 2; + guint8 type = type_has_references (klass, ftype) ? 1 : m_type_is_byref (ftype) ? 2 : 3; // Mark the bytes used by this fields type based on if it contains references or not. // Make sure there are no overlaps between object and non-object fields. @@ -1983,6 +2004,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ gboolean gc_aware_layout = FALSE; gboolean has_static_fields = FALSE; gboolean has_references = FALSE; + gboolean has_ref_fields = FALSE; gboolean has_static_refs = FALSE; MonoClassField *field; gboolean blittable; @@ -2009,6 +2031,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ min_align = klass->parent->min_align; /* we use | since it may have been set already */ has_references = klass->has_references | klass->parent->has_references; + has_ref_fields = klass->has_ref_fields | klass->parent->has_ref_fields; } else { min_align = 1; } @@ -2116,7 +2139,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ if (klass == mono_defaults.string_class) blittable = FALSE; - /* Compute klass->has_references */ + /* Compute klass->has_references and klass->has_ref_fields */ /* * Process non-static fields first, since static fields might recursively * refer to the class itself. @@ -2131,6 +2154,9 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ ftype = mono_type_get_basic_type_from_generic (ftype); if (type_has_references (klass, ftype)) has_references = TRUE; + + if (type_has_ref_fields (ftype)) + has_ref_fields = TRUE; } } @@ -2254,7 +2280,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ } ftype = mono_type_get_underlying_type (field->type); ftype = mono_type_get_basic_type_from_generic (ftype); - if (type_has_references (klass, ftype)) { + if (type_has_references (klass, ftype) || m_type_is_byref (ftype)) { if (field_offsets [i] % TARGET_SIZEOF_VOID_P) { mono_class_set_type_load_failure (klass, "Reference typed field '%s' has explicit offset that is not pointer-size aligned.", field->name); } @@ -2268,7 +2294,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ /* check for incorrectly aligned or overlapped by a non-object field */ guint8 *layout_check; - if (has_references) { + if (has_references || has_ref_fields) { layout_check = g_new0 (guint8, real_size); int invalid_field_offset; if (!validate_struct_fields_overlaps (layout_check, real_size, klass, field_offsets, top, &invalid_field_offset)) { @@ -2320,8 +2346,8 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ /* Emit info to help debugging */ g_print ("%s\n", mono_class_full_name (klass)); g_print ("%d %d %d %d\n", klass->instance_size, instance_size, klass->blittable, blittable); - g_print ("%d %d %d %d\n", klass->has_references, has_references, klass->packing_size, packing_size); - g_print ("%d %d\n", klass->min_align, min_align); + g_print ("%d %d %d %d\n", klass->has_references, has_references, klass->has_ref_fields, has_ref_fields); + g_print ("%d %d %d %d\n", klass->packing_size, packing_size, klass->min_align, min_align); for (i = 0; i < top; ++i) { field = &klass->fields [i]; if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)) @@ -2334,6 +2360,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ } klass->blittable = blittable; klass->has_references = has_references; + klass->has_ref_fields = has_ref_fields; klass->packing_size = packing_size; klass->min_align = min_align; klass->any_field_has_auto_layout = any_field_has_auto_layout; diff --git a/src/mono/mono/metadata/class-private-definition.h b/src/mono/mono/metadata/class-private-definition.h index 949c71d71f6e25..3d568bb8b92c1e 100644 --- a/src/mono/mono/metadata/class-private-definition.h +++ b/src/mono/mono/metadata/class-private-definition.h @@ -57,11 +57,12 @@ struct _MonoClass { guint packing_size : 4; guint ghcimpl : 1; /* class has its own GetHashCode impl */ guint has_finalize : 1; /* class has its own Finalize impl */ - /* next byte */ guint delegate : 1; /* class is a Delegate */ + /* next byte */ guint gc_descr_inited : 1; /* gc_descr is initialized */ guint has_cctor : 1; /* class has a cctor */ guint has_references : 1; /* it has GC-tracked references in the instance */ + guint has_ref_fields : 1; /* it has byref fields */ guint has_static_refs : 1; /* it has static fields that are GC-tracked */ guint no_special_static_fields : 1; /* has no thread/context static fields */ /* directly or indirectly derives from ComImport attributed class. diff --git a/src/tests/Loader/classloader/RefFields/InvalidCSharp.il b/src/tests/Loader/classloader/RefFields/InvalidCSharp.il index 768f603f22d55c..f45e7d3a7041bb 100644 --- a/src/tests/Loader/classloader/RefFields/InvalidCSharp.il +++ b/src/tests/Loader/classloader/RefFields/InvalidCSharp.il @@ -44,6 +44,36 @@ // .field public static string& Invalid // } +.class public explicit ansi sealed beforefieldinit InvalidCSharp.IntPtrRefFieldOverlap + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .field [0] public native int Field + .field [0] public int32& Invalid +} + +.class public sequential ansi sealed beforefieldinit InvalidCSharp.InnerValidByRef + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .field public int32& Field +} + +.class public explicit ansi sealed beforefieldinit InvalidCSharp.IntPtrOverlapWithInnerFieldType + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .field [0] public native int Field + // This field's valid Type would illegally overlap this type's first field using a precise GC. + .field [0] public valuetype InvalidCSharp.InnerValidByRef Invalid +} + .class public sequential ansi sealed beforefieldinit InvalidCSharp.WithRefField extends [System.Runtime]System.ValueType { diff --git a/src/tests/Loader/classloader/RefFields/Validate.cs b/src/tests/Loader/classloader/RefFields/Validate.cs index b286d51f2f6aae..22c72e212e121e 100644 --- a/src/tests/Loader/classloader/RefFields/Validate.cs +++ b/src/tests/Loader/classloader/RefFields/Validate.cs @@ -17,6 +17,8 @@ public static void Validate_Invalid_RefField_Fails() Assert.Throws(() => { var t = typeof(InvalidStructWithRefField); }); Assert.Throws(() => { var t = typeof(InvalidRefFieldAlignment); }); Assert.Throws(() => { var t = typeof(InvalidObjectRefRefFieldOverlap); }); + Assert.Throws(() => { var t = typeof(IntPtrRefFieldOverlap); }); + Assert.Throws(() => { var t = typeof(IntPtrOverlapWithInnerFieldType); }); } [Fact] diff --git a/src/tests/Loader/classloader/RefFields/Validate.csproj b/src/tests/Loader/classloader/RefFields/Validate.csproj index 96fedddd4bdda7..89e9243940878a 100644 --- a/src/tests/Loader/classloader/RefFields/Validate.csproj +++ b/src/tests/Loader/classloader/RefFields/Validate.csproj @@ -8,5 +8,6 @@ +