Skip to content

Commit b0c4728

Browse files
InlineArray's Equals and GetHashCode throw (#103612)
* InlineArray's Equals and GetHashCode throw The default Equals() and GetHashCode() throw for types marked with InlineArrayAttribute.
1 parent b0f3c19 commit b0c4728

File tree

7 files changed

+98
-4
lines changed

7 files changed

+98
-4
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,10 @@ public static void ThrowArgumentOutOfRangeException()
132132
{
133133
throw new ArgumentOutOfRangeException();
134134
}
135+
136+
public static void ThrowNotSupportedInlineArrayEqualsGetHashCode()
137+
{
138+
throw new NotSupportedException(SR.NotSupported_InlineArrayEqualsGetHashCode);
139+
}
135140
}
136141
}

src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ public static bool CanCompareValueTypeBits(MetadataType type, MethodDesc objectE
240240
if (type.ContainsGCPointers)
241241
return false;
242242

243+
if (type.IsInlineArray)
244+
return false;
245+
243246
if (type.IsGenericDefinition)
244247
return false;
245248

src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ public override MethodIL EmitIL()
6161

6262
ILEmitter emitter = new ILEmitter();
6363

64+
if (_owningType is MetadataType mdType)
65+
{
66+
// Types marked as InlineArray aren't supported by
67+
// the built-in Equals() or GetHashCode().
68+
if (mdType.IsInlineArray)
69+
{
70+
var stream = emitter.NewCodeStream();
71+
MethodDesc thrower = Context.GetHelperEntryPoint("ThrowHelpers", "ThrowNotSupportedInlineArrayEqualsGetHashCode");
72+
stream.EmitCallThrowHelper(emitter, thrower);
73+
return emitter.Link(this);
74+
}
75+
}
76+
6477
TypeDesc methodTableType = Context.SystemModule.GetKnownType("Internal.Runtime", "MethodTable");
6578
MethodDesc methodTableOfMethod = methodTableType.GetKnownMethod("Of", null);
6679

src/coreclr/vm/comutilnative.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,8 @@ BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
16241624
}
16251625

16261626
if (mt->ContainsPointers()
1627-
|| mt->IsNotTightlyPacked())
1627+
|| mt->IsNotTightlyPacked()
1628+
|| mt->GetClass()->IsInlineArray())
16281629
{
16291630
mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
16301631
return FALSE;
@@ -1677,14 +1678,17 @@ BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
16771678
return canCompareBitsOrUseFastGetHashCode;
16781679
}
16791680

1680-
extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable * mt)
1681+
extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
16811682
{
16821683
QCALL_CONTRACT;
16831684

16841685
BOOL ret = FALSE;
16851686

16861687
BEGIN_QCALL;
16871688

1689+
if (mt->GetClass()->IsInlineArray())
1690+
COMPlusThrow(kNotSupportedException, W("NotSupported_InlineArrayEqualsGetHashCode"));
1691+
16881692
ret = CanCompareBitsOrUseFastGetHashCode(mt);
16891693

16901694
END_QCALL;

src/libraries/System.Private.CoreLib/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,6 +3029,9 @@
30293029
<data name="NotSupported_IllegalOneByteBranch" xml:space="preserve">
30303030
<value>Illegal one-byte branch at position: {0}. Requested branch was: {1}.</value>
30313031
</data>
3032+
<data name="NotSupported_InlineArrayEqualsGetHashCode" xml:space="preserve">
3033+
<value>Calling built-in Equals() or GetHashCode() on type marked as InlineArray is invalid.</value>
3034+
</data>
30323035
<data name="NotSupported_KeyCollectionSet" xml:space="preserve">
30333036
<value>Mutating a key collection derived from a dictionary is not allowed.</value>
30343037
</data>

src/mono/mono/metadata/icall.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,11 @@ ves_icall_System_ValueType_InternalGetHashCode (MonoObjectHandle this_obj, MonoA
12521252

12531253
klass = mono_handle_class (this_obj);
12541254

1255+
if (m_class_is_inlinearray (klass)) {
1256+
mono_error_set_not_supported (error, "Calling built-in GetHashCode() on type marked as InlineArray is invalid.");
1257+
return FALSE;
1258+
}
1259+
12551260
if (mono_class_num_fields (klass) == 0)
12561261
return result;
12571262

@@ -1327,6 +1332,11 @@ ves_icall_System_ValueType_Equals (MonoObjectHandle this_obj, MonoObjectHandle t
13271332

13281333
klass = mono_handle_class (this_obj);
13291334

1335+
if (m_class_is_inlinearray (klass)) {
1336+
mono_error_set_not_supported (error, "Calling built-in Equals() on type marked as InlineArray is invalid.");
1337+
return FALSE;
1338+
}
1339+
13301340
if (m_class_is_enumtype (klass) && mono_class_enum_basetype_internal (klass) && mono_class_enum_basetype_internal (klass)->type == MONO_TYPE_I4)
13311341
return *(gint32*)mono_handle_get_data_unsafe (this_obj) == *(gint32*)mono_handle_get_data_unsafe (that);
13321342

src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public unsafe class Validate
4141
{
4242
// ====================== SizeOf ==============================================================
4343
[InlineArray(42)]
44-
struct FourtyTwoBytes
44+
struct FortyTwoBytes
4545
{
4646
byte b;
4747
}
@@ -50,11 +50,20 @@ struct FourtyTwoBytes
5050
public static void Sizeof()
5151
{
5252
Console.WriteLine($"{nameof(Sizeof)}...");
53-
Assert.Equal(42, sizeof(FourtyTwoBytes));
53+
Assert.Equal(42, sizeof(FortyTwoBytes));
5454
Assert.Equal(84, sizeof(MyArray<char>));
5555
}
5656

5757
// ====================== OneElement ==========================================================
58+
// These types are interesting since their layouts are
59+
// identical with or without the InlineArrayAttribute.
60+
61+
[InlineArray(1)]
62+
struct OneInt
63+
{
64+
public int i;
65+
}
66+
5867
[InlineArray(1)]
5968
struct OneObj
6069
{
@@ -65,6 +74,7 @@ struct OneObj
6574
public static void OneElement()
6675
{
6776
Console.WriteLine($"{nameof(OneElement)}...");
77+
Assert.Equal(sizeof(int), sizeof(OneInt));
6878
Assert.Equal(sizeof(nint), sizeof(OneObj));
6979
}
7080

@@ -393,4 +403,50 @@ public static void MonoGCDescOpt()
393403
Assert.Equal(i + 1, holder.arr[i].s);
394404
}
395405
}
406+
407+
struct StructHasFortyTwoBytesField
408+
{
409+
FortyTwoBytes _field;
410+
}
411+
412+
struct StructHasOneIntField
413+
{
414+
OneInt _field;
415+
}
416+
417+
[Fact]
418+
public static void InlineArrayEqualsGetHashCode_Fails()
419+
{
420+
Console.WriteLine($"{nameof(InlineArrayEqualsGetHashCode_Fails)}...");
421+
422+
Assert.Throws<NotSupportedException>(() =>
423+
{
424+
new OneInt().Equals(new OneInt());
425+
});
426+
427+
Assert.Throws<NotSupportedException>(() =>
428+
{
429+
new StructHasOneIntField().Equals(new StructHasOneIntField());
430+
});
431+
432+
Assert.Throws<NotSupportedException>(() =>
433+
{
434+
new FortyTwoBytes().Equals(new FortyTwoBytes());
435+
});
436+
437+
Assert.Throws<NotSupportedException>(() =>
438+
{
439+
new StructHasFortyTwoBytesField().Equals(new StructHasFortyTwoBytesField());
440+
});
441+
442+
Assert.Throws<NotSupportedException>(() =>
443+
{
444+
new OneInt().GetHashCode();
445+
});
446+
447+
Assert.Throws<NotSupportedException>(() =>
448+
{
449+
new FortyTwoBytes().GetHashCode();
450+
});
451+
}
396452
}

0 commit comments

Comments
 (0)