diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs
index 55adc4b8968246..c71b79f8dc0f9c 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs
@@ -132,5 +132,10 @@ public static void ThrowArgumentOutOfRangeException()
{
throw new ArgumentOutOfRangeException();
}
+
+ public static void ThrowNotSupportedInlineArrayEqualsGetHashCode()
+ {
+ throw new NotSupportedException(SR.NotSupported_InlineArrayEqualsGetHashCode);
+ }
}
}
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs
index cbb1c37c5ae6ea..8835a98168582a 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ComparerIntrinsics.cs
@@ -240,6 +240,9 @@ public static bool CanCompareValueTypeBits(MetadataType type, MethodDesc objectE
if (type.ContainsGCPointers)
return false;
+ if (type.IsInlineArray)
+ return false;
+
if (type.IsGenericDefinition)
return false;
diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs
index a4c967fbf967aa..babe171490aa0c 100644
--- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs
+++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs
@@ -61,6 +61,19 @@ public override MethodIL EmitIL()
ILEmitter emitter = new ILEmitter();
+ if (_owningType is MetadataType mdType)
+ {
+ // Types marked as InlineArray aren't supported by
+ // the built-in Equals() or GetHashCode().
+ if (mdType.IsInlineArray)
+ {
+ var stream = emitter.NewCodeStream();
+ MethodDesc thrower = Context.GetHelperEntryPoint("ThrowHelpers", "ThrowNotSupportedInlineArrayEqualsGetHashCode");
+ stream.EmitCallThrowHelper(emitter, thrower);
+ return emitter.Link(this);
+ }
+ }
+
TypeDesc methodTableType = Context.SystemModule.GetKnownType("Internal.Runtime", "MethodTable");
MethodDesc methodTableOfMethod = methodTableType.GetKnownMethod("Of", null);
diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp
index 143174cc24ce70..58c12cd319bf21 100644
--- a/src/coreclr/vm/comutilnative.cpp
+++ b/src/coreclr/vm/comutilnative.cpp
@@ -1624,7 +1624,8 @@ BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
}
if (mt->ContainsPointers()
- || mt->IsNotTightlyPacked())
+ || mt->IsNotTightlyPacked()
+ || mt->GetClass()->IsInlineArray())
{
mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
return FALSE;
@@ -1677,7 +1678,7 @@ BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
return canCompareBitsOrUseFastGetHashCode;
}
-extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable * mt)
+extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
{
QCALL_CONTRACT;
@@ -1685,6 +1686,9 @@ extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodT
BEGIN_QCALL;
+ if (mt->GetClass()->IsInlineArray())
+ COMPlusThrow(kNotSupportedException, W("NotSupported_InlineArrayEqualsGetHashCode"));
+
ret = CanCompareBitsOrUseFastGetHashCode(mt);
END_QCALL;
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 79653d2eb426a5..1f1669a04359cd 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -3029,6 +3029,9 @@
Illegal one-byte branch at position: {0}. Requested branch was: {1}.
+
+ Calling built-in Equals() or GetHashCode() on type marked as InlineArray is invalid.
+
Mutating a key collection derived from a dictionary is not allowed.
diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c
index be76808491569d..e1d9bacb5f7eea 100644
--- a/src/mono/mono/metadata/icall.c
+++ b/src/mono/mono/metadata/icall.c
@@ -1252,6 +1252,11 @@ ves_icall_System_ValueType_InternalGetHashCode (MonoObjectHandle this_obj, MonoA
klass = mono_handle_class (this_obj);
+ if (m_class_is_inlinearray (klass)) {
+ mono_error_set_not_supported (error, "Calling built-in GetHashCode() on type marked as InlineArray is invalid.");
+ return FALSE;
+ }
+
if (mono_class_num_fields (klass) == 0)
return result;
@@ -1327,6 +1332,11 @@ ves_icall_System_ValueType_Equals (MonoObjectHandle this_obj, MonoObjectHandle t
klass = mono_handle_class (this_obj);
+ if (m_class_is_inlinearray (klass)) {
+ mono_error_set_not_supported (error, "Calling built-in Equals() on type marked as InlineArray is invalid.");
+ return FALSE;
+ }
+
if (m_class_is_enumtype (klass) && mono_class_enum_basetype_internal (klass) && mono_class_enum_basetype_internal (klass)->type == MONO_TYPE_I4)
return *(gint32*)mono_handle_get_data_unsafe (this_obj) == *(gint32*)mono_handle_get_data_unsafe (that);
diff --git a/src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs b/src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs
index 885d2a08d8c91e..a41f493bf046b5 100644
--- a/src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs
+++ b/src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs
@@ -41,7 +41,7 @@ public unsafe class Validate
{
// ====================== SizeOf ==============================================================
[InlineArray(42)]
- struct FourtyTwoBytes
+ struct FortyTwoBytes
{
byte b;
}
@@ -50,11 +50,20 @@ struct FourtyTwoBytes
public static void Sizeof()
{
Console.WriteLine($"{nameof(Sizeof)}...");
- Assert.Equal(42, sizeof(FourtyTwoBytes));
+ Assert.Equal(42, sizeof(FortyTwoBytes));
Assert.Equal(84, sizeof(MyArray));
}
// ====================== OneElement ==========================================================
+ // These types are interesting since their layouts are
+ // identical with or without the InlineArrayAttribute.
+
+ [InlineArray(1)]
+ struct OneInt
+ {
+ public int i;
+ }
+
[InlineArray(1)]
struct OneObj
{
@@ -65,6 +74,7 @@ struct OneObj
public static void OneElement()
{
Console.WriteLine($"{nameof(OneElement)}...");
+ Assert.Equal(sizeof(int), sizeof(OneInt));
Assert.Equal(sizeof(nint), sizeof(OneObj));
}
@@ -393,4 +403,50 @@ public static void MonoGCDescOpt()
Assert.Equal(i + 1, holder.arr[i].s);
}
}
+
+ struct StructHasFortyTwoBytesField
+ {
+ FortyTwoBytes _field;
+ }
+
+ struct StructHasOneIntField
+ {
+ OneInt _field;
+ }
+
+ [Fact]
+ public static void InlineArrayEqualsGetHashCode_Fails()
+ {
+ Console.WriteLine($"{nameof(InlineArrayEqualsGetHashCode_Fails)}...");
+
+ Assert.Throws(() =>
+ {
+ new OneInt().Equals(new OneInt());
+ });
+
+ Assert.Throws(() =>
+ {
+ new StructHasOneIntField().Equals(new StructHasOneIntField());
+ });
+
+ Assert.Throws(() =>
+ {
+ new FortyTwoBytes().Equals(new FortyTwoBytes());
+ });
+
+ Assert.Throws(() =>
+ {
+ new StructHasFortyTwoBytesField().Equals(new StructHasFortyTwoBytesField());
+ });
+
+ Assert.Throws(() =>
+ {
+ new OneInt().GetHashCode();
+ });
+
+ Assert.Throws(() =>
+ {
+ new FortyTwoBytes().GetHashCode();
+ });
+ }
}