From 7bf3842202b278efa95e74e6e4a8fd16ef27e17f Mon Sep 17 00:00:00 2001 From: Iurie Caraion Date: Sun, 13 Sep 2026 17:43:58 +0300 Subject: [PATCH 1/2] Implement IsByRefLike on Emit symbol types SymbolType and TypeBuilderInstantiation did not override Type.IsByRefLike, so reading it on a type returned by MakeArrayType, MakePointerType, MakeByRefType or MakeGenericType on a TypeBuilder threw NotSupportedException ("Derived classes must provide an implementation"). #34846 added the override to the builder classes themselves but not to these internal types. SymbolType represents an array, pointer or byref type, which is never by-ref-like, so it returns false. TypeBuilderInstantiation forwards to its generic type definition: that is a builder, which returns false, or a runtime type when a runtime generic type is instantiated over a builder, as in typeof(Span<>).MakeGenericType(typeBuilder), which is by-ref-like. Fix #91532 Co-Authored-By: Claude Opus 5 (1M context) --- .../src/System/Reflection/Emit/SymbolType.cs | 2 ++ .../Emit/TypeBuilderInstantiation.cs | 1 + .../AssemblySaveTypeBuilderAPIsTests.cs | 10 ++++++ .../TypeBuilder/TypeBuilderIsByRefLike.cs | 32 +++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs index b154dac0d48b1d..ea9ddeafe80b30 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/SymbolType.cs @@ -259,6 +259,8 @@ internal void SetFormat(string format, int curIndex, int length) public override bool IsTypeDefinition => false; + public override bool IsByRefLike => false; + public override bool IsSZArray => _rank <= 1 && _isSzArray; public override Type? GetNullableUnderlyingType() => null; diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs index b7167fde85e323..86aac33074dd5b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilderInstantiation.cs @@ -217,6 +217,7 @@ public override Type? BaseType protected override TypeAttributes GetAttributeFlagsImpl() { return _genericType.Attributes; } public override bool IsTypeDefinition => false; + public override bool IsByRefLike => _genericType.IsByRefLike; public override bool IsSZArray => false; protected override bool IsArrayImpl() { return false; } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index 8063964e4473b7..b3fda4bc62c0e5 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -1010,5 +1010,15 @@ public void TestContainsGenericParametersOnMethodCtorOfConstructedGenericType() Assert.False(TypeBuilder.GetConstructor(instantiatedTypeBuilder2, constructorBuilder).ContainsGenericParameters); Assert.False(TypeBuilder.GetMethod(instantiatedTypeBuilder2, methodBuilder).ContainsGenericParameters); } + + [Theory] + [InlineData(typeof(Span<>), true)] + [InlineData(typeof(ReadOnlySpan<>), true)] + [InlineData(typeof(List<>), false)] + public void IsByRefLike_RuntimeGenericTypeInstantiatedOverTypeBuilder_MatchesGenericTypeDefinition(Type genericTypeDefinition, bool expected) + { + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + Assert.Equal(expected, genericTypeDefinition.MakeGenericType(type).IsByRefLike); + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderIsByRefLike.cs b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderIsByRefLike.cs index 3f03d268ca4702..c05576b13afac6 100644 --- a/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderIsByRefLike.cs +++ b/src/libraries/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderIsByRefLike.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Xunit; namespace System.Reflection.Emit.Tests @@ -13,5 +14,36 @@ public void IsByRefLikeReturnsFalse() TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); Assert.False(type.IsByRefLike); } + + [Fact] + public void IsByRefLike_TypesConstructedFromTypeBuilder_ReturnsFalse() + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters("T"); + + Type[] constructedTypes = + [ + type.MakeGenericType(typeof(int)), + type.MakeArrayType(), + type.MakeArrayType(2), + type.MakePointerType(), + type.MakeByRefType(), + typeParams[0].MakeArrayType(), + typeParams[0].MakePointerType(), + typeParams[0].MakeByRefType(), + ]; + + Assert.All(constructedTypes, t => Assert.False(t.IsByRefLike)); + } + + [Theory] + [InlineData(typeof(Span<>), true)] + [InlineData(typeof(ReadOnlySpan<>), true)] + [InlineData(typeof(List<>), false)] + public void IsByRefLike_RuntimeGenericTypeInstantiatedOverTypeBuilder_MatchesGenericTypeDefinition(Type genericTypeDefinition, bool expected) + { + TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public); + Assert.Equal(expected, genericTypeDefinition.MakeGenericType(type).IsByRefLike); + } } } From e3270946212a1a626eed6047ddd7ecfc0ca999e2 Mon Sep 17 00:00:00 2001 From: Iurie Caraion Date: Sun, 13 Sep 2026 17:44:20 +0300 Subject: [PATCH 2/2] Implement IsByRefLike on persisted Emit builders TypeBuilderImpl and GenericTypeParameterBuilderImpl, the builders returned by PersistedAssemblyBuilder, also threw NotSupportedException from Type.IsByRefLike. Return false, matching RuntimeTypeBuilder, RuntimeGenericTypeParameterBuilder and EnumBuilderImpl. Contributes to #91532 Co-Authored-By: Claude Opus 5 (1M context) --- .../Emit/GenericTypeParameterBuilderImpl.cs | 1 + .../System/Reflection/Emit/TypeBuilderImpl.cs | 1 + .../AssemblySaveTypeBuilderAPIsTests.cs | 23 +++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs index 916288b3f80918..378a31018dc39c 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/GenericTypeParameterBuilderImpl.cs @@ -83,6 +83,7 @@ public override Type[] GetGenericParameterConstraints() => public override bool IsGenericType => false; public override bool IsGenericParameter => true; public override bool IsConstructedGenericType => false; + public override bool IsByRefLike => false; public override bool ContainsGenericParameters => false; public override MethodBase? DeclaringMethod => _type.DeclaringMethod; public override Type? BaseType => _parent; diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 5f54a4c10de474..27ffad936b4bef 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -622,6 +622,7 @@ public override Type GetEnumUnderlyingType() throw new ArgumentException(SR.Argument_MustBeEnum); } } + public override bool IsByRefLike => false; public override bool IsSZArray => false; public override Guid GUID => throw new NotSupportedException(); public override Type? BaseType => _typeParent; diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index b3fda4bc62c0e5..42398fd3096308 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -1011,6 +1011,29 @@ public void TestContainsGenericParametersOnMethodCtorOfConstructedGenericType() Assert.False(TypeBuilder.GetMethod(instantiatedTypeBuilder2, methodBuilder).ContainsGenericParameters); } + [Fact] + public void IsByRefLike_TypeBuilderAndConstructedTypes_ReturnsFalse() + { + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + GenericTypeParameterBuilder[] typeParams = type.DefineGenericParameters("T"); + + Type[] types = + [ + type, + typeParams[0], + type.MakeGenericType(typeof(int)), + type.MakeArrayType(), + type.MakeArrayType(2), + type.MakePointerType(), + type.MakeByRefType(), + typeParams[0].MakeArrayType(), + typeParams[0].MakePointerType(), + typeParams[0].MakeByRefType(), + ]; + + Assert.All(types, t => Assert.False(t.IsByRefLike)); + } + [Theory] [InlineData(typeof(Span<>), true)] [InlineData(typeof(ReadOnlySpan<>), true)]