Skip to content

NativeAOT MakeGenericType fails to activate reflected generic type when generic parameter has self-referential interface constraint #126604

@vyacheslav-volkov

Description

@vyacheslav-volkov

Description

NativeAOT throws at runtime when creating a closed generic type via:

  • typeof(GenericType<,>).MakeGenericType(...)
  • Activator.CreateInstance(...)

if the generic type parameter has this constraint:

where TRequest : struct, IRequest<TRequest, TResponse>

The same code works if the self-referential interface constraint is removed and replaced with just:

where TRequest : struct

This looks incorrect, because the closed generic instantiation is valid and used at runtime with compatible type arguments, but NativeAOT does not preserve the required metadata/native code.

Reproduction Steps

public interface IGenericType<T, TResponse> where T : allows ref struct
{
    void Invoke(T value);
}

public interface IRequest<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TRequest, TResponse>
    where TRequest : IRequest<TRequest, TResponse>, allows ref struct;

public struct TestRequest : IRequest<TestRequest, string>;

public class GenericType<TRequest, TResponse> : IGenericType<TRequest, TResponse> where TRequest : struct, IRequest<TRequest, TResponse>
{
    public void Invoke(TRequest value) => Console.WriteLine($"value:{value}");
}

public class TypeHandler
{
    public void Handle<TRequest, TResponse>() where TRequest : allows ref struct
    {
        if (typeof(TRequest).IsValueType)
        {
            var genericType = (IGenericType<TRequest, TResponse>) Activator.CreateInstance(typeof(GenericType<,>).MakeGenericType(typeof(TRequest), typeof(TResponse)))!;
            genericType.Invoke(default!);
        }
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        new TypeHandler().Handle<TestRequest, string>();
    }
}
  1. Publish the repro with NativeAOT enabled.
  2. Run the produced executable.

Expected behavior

NativeAOT should handle constraints correctly in this case since it generates metadata if I remove the constraint.

Actual behavior

Runtime throws:
Unhandled exception. System.NotSupportedException: '...GenericType`2[...TestRequest,System.String]' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions