Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Support generic type parameter when created with TypeBuilder #112372

Merged
merged 11 commits into from
Feb 21, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal SignatureConstructedGenericType(Type genericTypeDefinition, Type[] type
protected sealed override bool IsArrayImpl() => false;
protected sealed override bool IsByRefImpl() => false;
public sealed override bool IsByRefLike => _genericTypeDefinition.IsByRefLike;
public sealed override bool IsEnum => false;
protected sealed override bool IsPointerImpl() => false;
public sealed override bool IsSZArray => false;
public sealed override bool IsVariableBoundArray => false;
Expand All @@ -50,6 +51,7 @@ public sealed override bool ContainsGenericParameters
}
}

protected sealed override bool IsValueTypeImpl() => _genericTypeDefinition.IsValueType;
internal sealed override SignatureType? ElementType => null;
public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
public sealed override Type GetGenericTypeDefinition() => _genericTypeDefinition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ protected SignatureGenericParameterType(int position)
protected sealed override bool IsArrayImpl() => false;
protected sealed override bool IsByRefImpl() => false;
public sealed override bool IsByRefLike => false;
public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsPointerImpl() => false;
public sealed override bool IsSZArray => false;
public sealed override bool IsVariableBoundArray => false;
public sealed override bool IsConstructedGenericType => false;
public sealed override bool IsGenericParameter => true;
public abstract override bool IsGenericMethodParameter { get; }
public sealed override bool ContainsGenericParameters => true;
protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);

internal sealed override SignatureType? ElementType => null;
public sealed override int GetArrayRank() => throw new ArgumentException(SR.Argument_HasToBeArrayClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protected SignatureHasElementType(SignatureType elementType)
protected abstract override bool IsArrayImpl();
protected abstract override bool IsByRefImpl();
public sealed override bool IsByRefLike => false;
public sealed override bool IsEnum => false;
protected abstract override bool IsPointerImpl();
public abstract override bool IsSZArray { get; }
public abstract override bool IsVariableBoundArray { get; }
Expand All @@ -27,6 +28,7 @@ protected SignatureHasElementType(SignatureType elementType)
public sealed override bool IsGenericTypeParameter => false;
public sealed override bool IsGenericMethodParameter => false;
public sealed override bool ContainsGenericParameters => _elementType.ContainsGenericParameters;
protected sealed override bool IsValueTypeImpl() => false;

internal sealed override SignatureType? ElementType => _elementType;
public abstract override int GetArrayRank();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public sealed override Type MakeArrayType(int rank)
public sealed override Type[] FindInterfaces(TypeFilter filter, object? filterCriteria) => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsContextfulImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override bool IsEnum => throw new NotSupportedException(SR.NotSupported_SignatureType);
public abstract override bool IsEnum { get; }
public sealed override bool IsEquivalentTo([NotNullWhen(true)] Type? other) => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override bool IsInstanceOfType([NotNullWhen(true)] object? o) => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsMarshalByRefImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
Expand All @@ -198,7 +198,7 @@ public sealed override Type MakeArrayType(int rank)
[Obsolete(Obsoletions.LegacyFormatterMessage, DiagnosticId = Obsoletions.LegacyFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public sealed override bool IsSerializable => throw new NotSupportedException(SR.NotSupported_SignatureType);
public sealed override bool IsSubclassOf(Type c) => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected sealed override bool IsValueTypeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
protected abstract override bool IsValueTypeImpl();

public sealed override StructLayoutAttribute StructLayoutAttribute => throw new NotSupportedException(SR.NotSupported_SignatureType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public TypeDelegator([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.
public override string? AssemblyQualifiedName => typeImpl.AssemblyQualifiedName;
public override Type? BaseType => typeImpl.BaseType;

public override int GetArrayRank() => typeImpl.GetArrayRank();

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder,
CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,8 +733,6 @@ public void MethodBuilderGetParametersReturnParameterTest()
Assert.True(method3.ReturnParameter.IsRetval);
}

public class BaseType<T> { }

[Fact]
public void GenericTypeWithTypeBuilderGenericParameter_UsedAsParent()
{
Expand All @@ -749,9 +747,53 @@ public void GenericTypeWithTypeBuilderGenericParameter_UsedAsParent()

Assert.NotNull(type.GetConstructor(Type.EmptyTypes)); // Default constructor created
}

[Fact]
public void CreateGenericTypeFromMetadataLoadContextSignatureTypes()
{
using TempFile file = TempFile.Create();

PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyAndModule(out ModuleBuilder module);

TypeBuilder childType = module.DefineType("Child");
TypeBuilder parentType = module.DefineType("Parent");

// Get List<T> from MLC and make both reference and value type fields from that.
using MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver());
Type listOfTType = mlc.CoreAssembly.GetType(typeof(List<>).FullName!);

// Currently MakeGenericSignatureType() must be used instead of MakeGenericType() for
// generic type parameters created with TypeBuilder.
Assert.Throws<ArgumentException>(() => listOfTType.MakeGenericType(childType));
Type listOfReferenceTypes = Type.MakeGenericSignatureType(listOfTType, childType);
parentType.DefineField("ReferenceTypeChildren", listOfReferenceTypes, FieldAttributes.Public);

// Pre-existing types can use MakeGenericType().
Type int32Type = mlc.CoreAssembly.GetType(typeof(int).FullName);
Type listOfValueTypes = listOfTType.MakeGenericType(int32Type);
parentType.DefineField("ValueTypeChildren", listOfValueTypes, FieldAttributes.Public);

parentType.CreateType();
childType.CreateType();

// Save and load the dynamically created assembly.
ab.Save(file.Path);
Module mlcModule = mlc.LoadFromAssemblyPath(file.Path).Modules.First();

Assert.Equal("Child", mlcModule.GetTypes()[0].Name);
Assert.Equal("Parent", mlcModule.GetTypes()[1].Name);

FieldInfo[] fields = mlcModule.GetTypes()[1].GetFields(BindingFlags.Public | BindingFlags.Instance);
Assert.Equal("ReferenceTypeChildren", fields[0].Name);
Assert.False(fields[0].FieldType.GetGenericArguments()[0].IsValueType);
Assert.Equal("ValueTypeChildren", fields[1].Name);
Assert.True(fields[1].FieldType.GetGenericArguments()[0].IsValueType);
}
}

// Test Types
public class BaseType<T> { }

public interface INoMethod
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,18 @@ namespace System.Reflection.Tests
{
public static class SignatureTypeTests
{
[Fact]
public static void IsSignatureType()
[Theory]
[MemberData(nameof(IsSignatureTypeTestData))]
public static void IsSignatureType(Type type, bool expected)
{
// Executing [Theory] logic manually. Signature Types cannot be used in theory data because Xunit preemptively invokes an unguarded
// System.Type pretty printer that invokes members that Signature Types don't support.
foreach (object[] pair in IsSignatureTypeTestData)
{
Type type = (Type)(pair[0]);
bool expected = (bool)(pair[1]);

Assert.Equal(expected, type.IsSignatureType);
}
Assert.Equal(expected, type.IsSignatureType);
}

public static IEnumerable<object[]> IsSignatureTypeTestData
{
get
{
// Standard reflection used as baseline.
yield return new object[] { typeof(int), false };
yield return new object[] { typeof(int).MakeArrayType(), false };
yield return new object[] { typeof(int).MakeArrayType(1), false };
Expand All @@ -37,6 +31,7 @@ public static IEnumerable<object[]> IsSignatureTypeTestData
yield return new object[] { typeof(List<>).MakeGenericType(typeof(int)), false };
yield return new object[] { typeof(List<>).GetGenericArguments()[0], false };

// SignatureTypes.
Type sigType = Type.MakeGenericMethodParameter(2);

yield return new object[] { sigType, true };
Expand All @@ -48,7 +43,9 @@ public static IEnumerable<object[]> IsSignatureTypeTestData
yield return new object[] { typeof(List<>).MakeGenericType(sigType), true };

yield return new object[] { Type.MakeGenericSignatureType(typeof(List<>), typeof(int)), true };
yield return new object[] { Type.MakeGenericSignatureType(typeof(List<>), typeof(int)).GetGenericArguments()[0], false };
yield return new object[] { Type.MakeGenericSignatureType(typeof(List<>), sigType), true };
yield return new object[] { Type.MakeGenericSignatureType(typeof(List<>), sigType).GetGenericArguments()[0], true };
}
}

Expand Down Expand Up @@ -211,6 +208,8 @@ public static void MakeGenericMethodParameter(int position)
Assert.False(t.IsGenericTypeParameter);
Assert.True(t.IsGenericMethodParameter);
Assert.Equal(position, t.GenericParameterPosition);
Assert.Throws<NotSupportedException>(() => t.IsValueType);
Assert.Throws<NotSupportedException>(() => t.IsEnum);
TestSignatureTypeInvariants(t);
}

Expand All @@ -231,6 +230,8 @@ public static void MakeSignatureArrayType()
Assert.True(t.IsArray);
Assert.True(t.IsSZArray);
Assert.Equal(1, t.GetArrayRank());
Assert.False(t.IsValueType);
Assert.False(t.IsEnum);

Type et = t.GetElementType();
Assert.True(et.IsSignatureType);
Expand All @@ -253,6 +254,8 @@ public static void MakeSignatureMdArrayType(int rank)
Assert.True(t.IsArray);
Assert.True(t.IsVariableBoundArray);
Assert.Equal(rank, t.GetArrayRank());
Assert.False(t.IsValueType);
Assert.False(t.IsEnum);

TestSignatureTypeInvariants(t);
}
Expand All @@ -263,6 +266,8 @@ public static void MakeSignatureByRefType()
Type t = Type.MakeGenericMethodParameter(5);
t = t.MakeByRefType();
Assert.True(t.IsByRef);
Assert.False(t.IsValueType);
Assert.False(t.IsEnum);

Type et = t.GetElementType();
Assert.True(et.IsSignatureType);
Expand All @@ -280,6 +285,8 @@ public static void MakeSignaturePointerType()
Type t = Type.MakeGenericMethodParameter(5);
t = t.MakePointerType();
Assert.True(t.IsPointer);
Assert.False(t.IsValueType);
Assert.False(t.IsEnum);

Type et = t.GetElementType();
Assert.True(et.IsSignatureType);
Expand All @@ -305,6 +312,8 @@ public static void MakeSignatureConstructedGenericType(Type genericTypeDefinitio
Assert.True(t.IsConstructedGenericType);
Assert.Equal(genericTypeDefinition, t.GetGenericTypeDefinition());
Assert.Equal(1, t.GenericTypeArguments.Length);
Assert.Equal(genericTypeDefinition.IsValueType, t.IsValueType);
Assert.False(t.IsEnum);

Type et = t.GenericTypeArguments[0];
Assert.True(et.IsSignatureType);
Expand Down Expand Up @@ -408,6 +417,8 @@ public static void Moo<M>(int p1, int p2) where M : NoOneSubclassesThisEither {
private class NoOneSubclasses { }
private class NoOneSubclassesThisEither { }

private enum MyEnum { }

private static void TestSignatureTypeInvariants(Type type)
{
Assert.True(type.IsSignatureType);
Expand Down