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

Deduce closed generic type usages based GetGenericTypeDefinition/MakeGenericType #3080

Open
kant2002 opened this issue Oct 24, 2022 · 6 comments

Comments

@kant2002
Copy link
Contributor

How complicated would be to teach linker that GetGenericTypeDefinition produce generic type definition for well-known type, and that MakeGenericType produce well known specialized type.

// Create closed generic of type int
var genericInt = typeof(GenericType<int>);
var instance = Activator.CreateInstance(genericInt);
Console.WriteLine(instance.ToString());

// Create closed generic of type string
var genericString = typeof(GenericType<string>);
instance = Activator.CreateInstance(genericString);
Console.WriteLine(instance.ToString());

// Create closed generic of type X
// works because string and X is reference type.
var genericDefinition = genericString.GetGenericTypeDefinition(); // typeof(GenericType<>);
var genericX = genericDefinition.MakeGenericType(typeof(X)); // typeof(GenericType<X>);
instance = Activator.CreateInstance(genericX);
Console.WriteLine(instance.ToString());

// Not working, because code for GenericType<long> is different then
// GenericType<int>
var genericLong = genericDefinition.MakeGenericType(typeof(long));
instance = Activator.CreateInstance(genericLong);
Console.WriteLine(instance.ToString());

class X
{
    public override string ToString() => "Mr X";
}


class GenericType<T>
{
    public override string ToString() => "Mr " + typeof(T).FullName;
}

and if complicated, why it's complicated?

@marek-safar
Copy link
Contributor

Just to be sure we are on the same page, what is not working here?

@vitek-karas
Copy link
Member

I assume the question is mostly related to AOT, right? Since the above will work just fine with trimming alone.

The GetGenericTypeDefinition would be pretty simple to implement - and there would be potential benefits even for trimming itself. (If the generic type parameter has annotation the subsequent MakeGenericType would know about it and could validate it avoiding warnings which are generated today).

The AOT specific ability to track instantiations made via MakeGenericType - definitely doable, so far we simply didn't implement this.

What would help is if you have pointers to existing code with such patterns, so that we can rationalize the importance of having this support.

@kant2002
Copy link
Contributor Author

Yes. That’s related to AOT, sorry for missing that, I understand that regular trimming unrelated. I will look for real world code. I actually come up with that pattern when try to create presentation about shortcomings of NativeAOT. I will look at EF Core and FSharp for similar patterns.

If that’s reasonably easy, I would like to implement this, since that allow me more confidently have a message that unless you are doing some really arcane meta programming you are in loving hands of NativeAOT

@vitek-karas
Copy link
Member

If you want to give a try - definitely go for it. I'll try to help as much as I can.

@MichalStrehovsky
Copy link
Member

I'd like to see the real-world use cases for this - suggestions for this kind of analysis do come up (dotnet/runtime#81204), and it can be analyzed, but if all the type parameters are known, it can be rewritten to not use reflection at all (like in your example). That's a much better fix and less analyzer code to maintain.

@vitek-karas
Copy link
Member

Thanks @MichalStrehovsky for pointing out that this would only work in very limited situations. Basically only if the types are statically known - in which case rewriting it without reflection is definitely the better approach. The only scenario which might be slightly useful and doesn't require statically known types is something like this:

void TestMethod([DynamicallyAccessedMembers(PublicParameterlessConstructor)] Type type, Type elementType)
{
    var genericType = type.GetGenericTypeDefinition();
    var newType = genericType.MakeGenericType(elementType); // Warns - since it can't tell if the generic type parameters are annotated
    var inst = Activator.CreateInstance(newType);  // Currently this will warn, if we recognized GetGenericTypeDefinition we could make this not warn in this case.
}

It's questionable how much value there is in supporting the above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants