diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp index 2a3e066d8adbac..4e512fa7cb38f6 100644 --- a/src/coreclr/vm/genericdict.cpp +++ b/src/coreclr/vm/genericdict.cpp @@ -848,7 +848,12 @@ Dictionary::PopulateEntry( th = th.GetMethodTable()->GetMethodTableMatchingParentClass(declaringType.AsMethodTable()); } - th.GetMethodTable()->EnsureInstanceActive(); + if (!th.IsTypeDesc()) + { + MethodTable* pMT = th.AsMethodTable(); + _ASSERTE(pMT != NULL); + pMT->EnsureInstanceActive(); + } result = (CORINFO_GENERIC_HANDLE)th.AsPtr(); break; diff --git a/src/coreclr/vm/unsafeaccessors.cpp b/src/coreclr/vm/unsafeaccessors.cpp index 08a521d3ac3715..042fa4ebc1f853 100644 --- a/src/coreclr/vm/unsafeaccessors.cpp +++ b/src/coreclr/vm/unsafeaccessors.cpp @@ -1076,6 +1076,23 @@ bool MethodDesc::TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMET context.TargetTypeSig = context.DeclarationMetaSig.GetArgProps(); (void)context.TargetTypeSig.PeekElemType(&firstArgCorType); firstArgType = context.DeclarationMetaSig.GetLastTypeHandleThrowing(); + + // For instance member access, the first argument is the receiver ("this"). + // For value types the receiver must be passed byref. A byref receiver can also occur for reference + // types depending on the declaration signature. + // + // However, when emitting the metadata token for the accessed member, the owning type must not be + // a byref. Keeping the byref here can lead to incorrect shared generic context lookups at runtime. + // + // No special handling is required for other forms of type parameterization here: + // * Arrays and generic instantiations are part of the owning type identity and must not be stripped. + // * Other forms (eg. pointers) are not valid target types and will be rejected by ValidateTargetType(). + if ((context.Kind == UnsafeAccessorKind::Method || context.Kind == UnsafeAccessorKind::Field) + && firstArgCorType == ELEMENT_TYPE_BYREF) + { + // Consume byref token. + (void)context.TargetTypeSig.GetElemType(nullptr); + } } // Using the kind type, perform the following: diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs index df95ad1bb194a5..0b46c2c0f09f77 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.Generics.cs @@ -9,7 +9,31 @@ using Xunit; -struct Struct { } +struct Struct +{ + public Type Value; + private void SetType(Type type) + { + Value = type; + } + private void SetType() + { + Value = typeof(T); + } +} + +struct GenericStruct +{ + public Type Value; + private void SetType(Type type) + { + Value = type; + } + private void SetType() + { + Value = typeof(U); + } +} interface I1 { } @@ -112,6 +136,12 @@ static class Accessors [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "CreateMessage")] public extern static string CreateMessage(GenericBase b, V v); + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SetType")] + public extern static void SetType(ref GenericStruct s, Type type); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SetType")] + public extern static void SetType(ref GenericStruct s); + [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ElementType")] public extern static Type ElementType(MyList l); @@ -128,6 +158,15 @@ static class Accessors public extern static ref V GetPrivateStaticField(MyList d); } + static class Accessors + { + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SetType")] + public extern static void SetType(ref Struct s); + + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SetType")] + public extern static void SetType(ref Struct s, Type type); + } + [Fact] public static void Verify_Generic_AccessStaticFieldClass() { @@ -459,6 +498,68 @@ public static void Verify_Generic_GenericTypeGenericStaticMethod() } } + [Fact] + public static void Verify_Generic_Structs() + { + Console.WriteLine($"Running {nameof(Verify_Generic_Structs)}"); + + { + Struct s = new(); + Accessors.SetType(ref s, typeof(int)); + Assert.Equal(typeof(int), s.Value); + Accessors.SetType(ref s, typeof(string)); + Assert.Equal(typeof(string), s.Value); + Accessors.SetType(ref s, typeof(Struct)); + Assert.Equal(typeof(Struct), s.Value); + Accessors.SetType(ref s, typeof(GenericStruct)); + Assert.Equal(typeof(GenericStruct), s.Value); + Accessors.SetType(ref s, typeof(GenericStruct)); + Assert.Equal(typeof(GenericStruct), s.Value); + } + + { + Struct s = new(); + Accessors.SetType(ref s); + Assert.Equal(typeof(int), s.Value); + Accessors.SetType(ref s); + Assert.Equal(typeof(string), s.Value); + Accessors.SetType(ref s); + Assert.Equal(typeof(Struct), s.Value); + Accessors.SetType>(ref s); + Assert.Equal(typeof(GenericStruct), s.Value); + Accessors.SetType>(ref s); + Assert.Equal(typeof(GenericStruct), s.Value); + } + + { + GenericStruct s = new(); + Accessors.SetType(ref s, typeof(int)); + Assert.Equal(typeof(int), s.Value); + Accessors.SetType(ref s); + Assert.Equal(typeof(string), s.Value); + Accessors.SetType(ref s); + Assert.Equal(typeof(Struct), s.Value); + Accessors.SetType>(ref s); + Assert.Equal(typeof(GenericStruct), s.Value); + Accessors.SetType>(ref s); + Assert.Equal(typeof(GenericStruct), s.Value); + } + + { + GenericStruct s = new(); + Accessors.SetType(ref s, typeof(int)); + Assert.Equal(typeof(int), s.Value); + Accessors.SetType(ref s); + Assert.Equal(typeof(string), s.Value); + Accessors.SetType(ref s); + Assert.Equal(typeof(Struct), s.Value); + Accessors.SetType>(ref s); + Assert.Equal(typeof(GenericStruct), s.Value); + Accessors.SetType>(ref s); + Assert.Equal(typeof(GenericStruct), s.Value); + } + } + class MethodWithConstraints { private string M() where T : U, I2