dotnet / runtime

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

TrimmerRootDescriptor is not able to preserve generic instantiations #95058

Closed hez2010 closed 11 months ago

hez2010 commented 1 year ago

Description

Assuming we have the below lines in root.xml:

<linker>
  <assembly fullname="MyAssembly">
    <type fullname="Foo`1[[System.Int32,System.Private.CoreLib]]" preserve="all" />
  </assembly>
</linker>

It will fail to resolve the type when compiling with NativeAOT because:

  1. In SplitFullName it splits the full name into namespace Foo`1[[System and type Int32,System.Private.CoreLib]]: https://github.com/dotnet/runtime/blob/3b61ad9e3acb6302fd2fcef4947373475a0dcc29/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs#L795
  2. Even if it was split correctly, it still failed to resolve the type because the type name Foo`1[[System.Int32,System.Private.CoreLib]] was not in the name cache (there was only Foo`1 in the name cache, so any instantiations will fail to resolve): https://github.com/dotnet/runtime/blob/3b61ad9e3acb6302fd2fcef4947373475a0dcc29/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs#L324

This is preventing us to move from rd.xml to TrimmerRootDescriptor. TrimmerRootDescriptor should handle generic instantiations as well.

/cc: @MichalStrehovsky

Reproduction Steps

Create a console project MyAssembly with code:

var t = Console.ReadLine();
Console.WriteLine(typeof(Foo<>).MakeGenericType(Type.GetType(t!)!));

class Foo<T>
{
    public override string ToString()
    {
        return typeof(T).ToString();
    }
}

and TrimmerRootDescriptor:

<linker>
  <assembly fullname="MyAssembly">
    <type fullname="Foo`1[[System.Int32,System.Private.CoreLib]]" preserve="all" />
  </assembly>
</linker>

Run it and input System.Int32.

Expected behavior

Print Foo`1[System.Int32]

Actual behavior

warning IL2008: Could not resolve type 'Foo`1[[System.Int32,System.Private.CoreLib]]'
Unhandled Exception: System.NotSupportedException: 'Foo`1[System.Int32]' 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. For more information see https://aka.ms/nativeaot-compatibility

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response

ghost commented 1 year ago

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas See info in area-owners.md if you want to be subscribed.

Issue Details
### Description Assuming we have the below lines in root.xml: ```xml ``` It will fail to resolve the type when compiling with NativeAOT because: 1. In `SplitFullName` it splits the full name into namespace ``Foo`1[[System`` and type `Int32,System.Private.CoreLib]]`: https://github.com/dotnet/runtime/blob/3b61ad9e3acb6302fd2fcef4947373475a0dcc29/src/coreclr/tools/Common/Compiler/ProcessLinkerXmlBase.cs#L795 2. Even if it was split correctly, it still failed to resolve the type because the type name ``Foo`1[[System.Int32,System.Private.CoreLib]]`` was not in the name cache (there was only ``Foo`1`` in the name cache, so any instantiations will fail to resolve): https://github.com/dotnet/runtime/blob/3b61ad9e3acb6302fd2fcef4947373475a0dcc29/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs#L324 This is preventing us to move from `rd.xml` to `TrimmerRootDescriptor`. `TrimmerRootDescriptor` should handle generic instantiations as well. /cc: @MichalStrehovsky ### Reproduction Steps Create a console project `MyAssembly` with code: ```cs var t = Console.ReadLine(); Console.WriteLine(typeof(Foo<>).MakeGenericType(Type.GetType(t!)!)); class Foo { public override string ToString() { return typeof(T).ToString(); } } ``` and TrimmerRootDescriptor: ```xml ``` Run it and input `System.Int32`. ### Expected behavior Print ``Foo`1[System.Int32]`` ### Actual behavior ``` warning IL2008: Could not resolve type 'Foo`1[[System.Int32,System.Private.CoreLib]]' ``` ``` Unhandled Exception: System.NotSupportedException: 'Foo`1[System.Int32]' 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. For more information see https://aka.ms/nativeaot-compatibility ``` ### Regression? _No response_ ### Known Workarounds _No response_ ### Configuration _No response_ ### Other information _No response_
Author: hez2010
Assignees: -
Labels: `untriaged`, `area-NativeAOT-coreclr`
Milestone: -
MichalStrehovsky commented 1 year ago

This matches the behavior of IL linker. It's not legal in the descriptors file format to put instantiated generics as type name. The problem is more pronounced for generic methods. We'd need a file format change to do this.

Cc @dotnet/illink-contrib

vitek-karas commented 1 year ago

Ideally you would not use the XML files at all, can you replace it with a DynamicDependency attribute instead? Or you can use Type.GetType("") in code somewhere, that should work for all kinds of types (and will use the Reflection syntax). Both should work "cross assembly", so if you add those somewhere reachable from your main, that should be enough.

hez2010 commented 1 year ago

Ideally you would not use the XML files at all

Yeah that's the ideal case I want to see too. But in the real world you will have to workaround against some 3rd-party even 1st-party libraries using those XML files. Now that we retired rd.xml, I think the recommended approach TrimmerRootDescriptor should at least on pair with rd.xml on functionality.

MichalStrehovsky commented 1 year ago

Using the Type.GetType trick to ensure the type is constructed is probably the best. The syntax can be validated during F5 debug time on a JIT-based runtime instead of worrying one got the XML wrong.

The experience with any of the XML formats is extremely poor (first one can never be sure whether the file is getting picked up at all, and then there's no way to troubleshoot it if things don't work). We don't optimize for them, they're last resort option and the user is already on pretty much unsupported paths (we cannot help authoring these XML files).

hez2010 commented 1 year ago

Using the Type.GetType trick to ensure the type is constructed is probably the best.

Does this also work for delegate marshalling metadata and generic methods?

I just tried

Type.GetType("Foo`1[[System.Int32,System.Private.CoreLib]]")!.GetMethod("Test")!.MakeGenericMethod(typeof(int))

against

class Foo<T>
{
    public void Test<U>()
    {
        Console.WriteLine(typeof(U).ToString());
    }
}

but got

System.NotSupportedException: 'Foo`1[System.Int32].Test[System.Int32]()' is missing native code. MethodInfo.MakeGenericMethod() is not compatible with AOT compilation. Inspect and fix AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility

Apparently it's not working without https://github.com/dotnet/runtime/issues/81204

MichalStrehovsky commented 1 year ago

Does this also work for delegate marshalling metadata and generic methods?

I thought we're only discussing accepting generics in the fullname. If the scope is "what is the replacement for RD.XML", we don't have any. The position is really that if something is generating warnings, it may not work, it needs to be written so that it doesn't warn, and that's it. RD.XML will work in .NET 8 for workarounds. We don't plan to build a long-term path for that experience. TrimmerRootDescriptors only exist because they survived from the time when no static analysis was done and unfortunately we documented them.

MichalStrehovsky commented 11 months ago

I'm going to close this. I was looking at yet another customer-created RD.XML that had nonsensical things in it, like rooting all of CoreLib (that was then causing problems for the customer). Very few people understand how to use this. We don't want to build experiences around sidecar XML files that people cargo cult around.