Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support generic type parameter when created with TypeBuilder #112372

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public sealed override bool ContainsGenericParameters
}
}

protected sealed override bool IsValueTypeImpl() => _genericTypeDefinition.IsValueType;
jkotas marked this conversation as resolved.
Show resolved Hide resolved
steveharter marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -26,6 +26,7 @@ protected SignatureGenericParameterType(int position)
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 @@ -27,6 +27,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 @@ -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();
jkotas marked this conversation as resolved.
Show resolved Hide resolved

[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
jkotas marked this conversation as resolved.
Show resolved Hide resolved
// 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,10 +43,53 @@ 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 };
}
}

[Theory]
[MemberData(nameof(IsValueTypeTestData))]
public static void IsValueType(Type type, bool expected)
{
Assert.Equal(expected, type.IsValueType);
}

public static IEnumerable<object[]> IsValueTypeTestData
{
get
{
// Standard reflection used as baseline.
yield return new object[] { typeof(int), true };
yield return new object[] { typeof(object), false };
yield return new object[] { typeof(int).MakeArrayType(), false };
yield return new object[] { typeof(int).MakeByRefType(), false };
yield return new object[] { typeof(int).MakePointerType(), false };
yield return new object[] { typeof(List<>).GetGenericArguments()[0], false };
yield return new object[] { typeof(List<>).MakeGenericType(typeof(int)), false };
yield return new object[] { typeof(List<>).MakeGenericType(typeof(object)), false };
yield return new object[] { typeof(SignatureTypeTests).GetMethod(nameof(MyGenericValueTypeMethod), BindingFlags.NonPublic | BindingFlags.Static).GetGenericArguments()[0], true };
yield return new object[] { typeof(SignatureTypeTests).GetMethod(nameof(MyGenericRefTypeMethod), BindingFlags.NonPublic | BindingFlags.Static).GetGenericArguments()[0], false };
yield return new object[] { typeof(List<>).MakeGenericType(typeof(int)).GetGenericArguments()[0], true };
yield return new object[] { typeof(List<>).MakeGenericType(typeof(object)).GetGenericArguments()[0], false };

// SignatureTypes.
steveharter marked this conversation as resolved.
Show resolved Hide resolved
Type sigType = Type.MakeGenericMethodParameter(2);

yield return new object[] { sigType.MakeArrayType(), false };
yield return new object[] { sigType.MakeByRefType(), false };
yield return new object[] { sigType.MakePointerType(), false };

// These have normal generic argument types, not SignatureTypes.
yield return new object[] { Type.MakeGenericSignatureType(typeof(List<>), typeof(int)).GetGenericArguments()[0], true };
yield return new object[] { Type.MakeGenericSignatureType(typeof(List<>), typeof(object)).GetGenericArguments()[0], false };
}
}

private static void MyGenericValueTypeMethod<T>() where T : struct { }
private static void MyGenericRefTypeMethod<T>() where T : class { }

jkotas marked this conversation as resolved.
Show resolved Hide resolved
[Fact]
public static void GetMethodWithGenericParameterCount()
{
Expand Down Expand Up @@ -211,6 +249,7 @@ public static void MakeGenericMethodParameter(int position)
Assert.False(t.IsGenericTypeParameter);
Assert.True(t.IsGenericMethodParameter);
Assert.Equal(position, t.GenericParameterPosition);
Assert.Throws<NotSupportedException>(() => t.IsValueType);
TestSignatureTypeInvariants(t);
}

Expand All @@ -237,6 +276,7 @@ public static void MakeSignatureArrayType()
Assert.True(et.IsGenericParameter);
Assert.False(et.IsGenericTypeParameter);
Assert.True(et.IsGenericMethodParameter);
Assert.Throws<NotSupportedException>(() => et.IsValueType);
steveharter marked this conversation as resolved.
Show resolved Hide resolved
Assert.Equal(5, et.GenericParameterPosition);

TestSignatureTypeInvariants(t);
steveharter marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -312,6 +352,7 @@ public static void MakeSignatureConstructedGenericType(Type genericTypeDefinitio
Assert.False(et.IsGenericTypeParameter);
Assert.True(et.IsGenericMethodParameter);
Assert.Equal(5, et.GenericParameterPosition);
Assert.Throws<NotSupportedException>(() => et.IsValueType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move up before Type et = t.GenericTypeArguments[0]; line and change to Assert.False.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I wanted to make sure the new code that was added for IsValueType\IsEnum throws when the generic parameter is a signature type.

If I compare t with t.IsValueType that is just testing normal reflection and will return true for Span<sigtype> and false for the List<sigtype>.


TestSignatureTypeInvariants(t);
});
Expand Down
Loading