dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.81k stars 4.61k forks source link

Make Generic Type allows you to create types with abstract classes when the constrant should block it #101962

Open Faolan-Rad opened 3 months ago

Faolan-Rad commented 3 months ago

Description

You can create types with new() constraint which should block the use of abstract classes but it allows them any way though the use of the MakeGenericType and then you can instantiate them through activator

Reproduction Steps

public abstract class W { 
    /// This makes the bug happen
    public W()
    { }
}

public sealed class Trains<T> where T : W, new() { }

internal class Program
{
    static void Main(string[] args)
    { 
        //var e = new Trains<W>(); // This line has expected must be a none-abstract type 
        var e = Activator.CreateInstance(typeof(Trains<>)
            .MakeGenericType(typeof(W))); // This runs add creates the invalid type anyway
        Console.WriteLine(e?.ToString());
    }
}

Expected behavior

Probably for MakeGenericType to throw an invalid type exception

Actual behavior

It lets the type get created and it can be instated with the activator without a problem

Regression?

It happens from 6 - 8 did not test any more

Known Workarounds

You could add your own type check before hand

Configuration

.net 6 - 8

Other information

MakeGenericType probably just add a is abstract check for when it is a new() constraint

Faolan-Rad commented 3 months ago

I did a test it also happens with method reflection

using System.Reflection;

public abstract class W {
    /// This makes the bug happen
    public W()
    { }
}

public sealed class Trains<T> where T : W, new() { }

internal class Program
{

    public static T Create<T>() where T : W , new()
    {
        return new T();
    }

    static void Main(string[] args)
    {
        var shouldNotGive = typeof(Program).GetMethod("Create", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(typeof(W));
        shouldNotGive.Invoke(null, Array.Empty<object>());// Will Try and run the method and get a MissingMethodException: Cannot dynamically create an instance of type 'W'. Reason: Cannot create an abstract class.
        //var e = new Trains<W>(); // This line has expected must be a none-abstract type 
        var e = Activator.CreateInstance(typeof(Trains<>)
            .MakeGenericType(typeof(W))); // This runs add creates the invalid type anyway
        Console.WriteLine(e?.ToString());
    }
}
steveisok commented 3 months ago

/cc @fanyang-mono

dotnet-policy-service[bot] commented 2 months ago

Tagging subscribers to this area: @dotnet/area-system-reflection See info in area-owners.md if you want to be subscribed.

jkotas commented 2 months ago

This is type loader problem. The repro happens to use reflection, but it would repro with plain IL too.

https://github.com/dotnet/runtime/pull/101963 has a proposed fix.

steveisok commented 1 week ago

Since https://github.com/dotnet/runtime/pull/101963 is a breaking change, we'll apply it to .NET 10.