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

Microsoft.Extensions.Configuration APIs haven't been AOT-annotated #71654

Closed
Tracked by #64015
kant2002 opened this issue Jul 5, 2022 · 10 comments · Fixed by #73853
Closed
Tracked by #64015

Microsoft.Extensions.Configuration APIs haven't been AOT-annotated #71654

kant2002 opened this issue Jul 5, 2022 · 10 comments · Fixed by #73853

Comments

@kant2002
Copy link
Contributor

kant2002 commented Jul 5, 2022

Consider this small application

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;

var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var appSettings = GetAppSettings(config);
Console.WriteLine($"{appSettings.Database}: {appSettings.ConnectionString}");

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern",
    Justification = "AppSettings is consists only from primitive type and thus will never be trimmed")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:UnrecognizedReflectionPattern",
    Justification = "AppSettings is consists only from primitive type and thus will never be trimmed")]
AppSettings GetAppSettings(IConfiguration config)
{
    return config.Get<AppSettings>();
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public class AppSettings
{
    public string? ConnectionString { get; set; }

    public DatabaseServer Database { get; set; } = DatabaseServer.None;
}

public enum DatabaseServer
{
    None,
    SqlServer,
    PostgreSql,
    MySql
}

With following packages

dotnet add package Microsoft.Extensions.Configuration.Binder -v 7.0.0-*
dotnet add package Microsoft.Extensions.Configuration.Json -v 7.0.0-*

and `appsettings.json

{
    "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;SslMode=None;ConnectionReset=false;ConnectionIdlePingTime=900;ConnectionIdleTimeout=0;AutoEnlist=false;DefaultCommandTimeout=0;ConnectionTimeout=0;IgnorePrepare=false;",
    "Database": "mysql"
}

ILC spill warnings

ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.AttemptBindToCollectionInterfaces(Type,IConfiguration,BinderOptions): Using member 'System.Type.MakeGenericType(Type[]
)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.AttemptBindToCollectionInterfaces(Type,IConfiguration,BinderOptions): Using member 'System.Type.MakeGenericType(Type[]
)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.BindArray(Type,IEnumerable,IConfiguration,BinderOptions): Using member 'System.Array.CreateInstance(Type,Int32)' which
 has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.BindToCollection(Type,IConfiguration,BinderOptions): Using member 'System.Type.MakeGenericType(Type[])' which has 'Req
uiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
/_/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EnumConverter.cs(174): AOT analysis warning IL3050: System.ComponentModel.EnumConverter.ConvertTo(ITypeDescriptorContext,CultureI
nfo,Object,Type): Using member 'System.Enum.GetValues(Type)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. It might not be possible to create an array of the enum type a
t runtime. Use the GetValues<TEnum> overload instead. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]

From my understanding AppSettings is safe to map IConfiguration and I want express that. But as you can see all my silly attempts fails.

@kant2002
Copy link
Contributor Author

kant2002 commented Jul 5, 2022

This is boils down to patterns like this

collectionInterface = FindOpenGenericInterface(typeof(IReadOnlyDictionary<,>), type);
if (collectionInterface != null)
{
Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(type.GenericTypeArguments[0], type.GenericTypeArguments[1]);
object instance = Activator.CreateInstance(dictionaryType)!;
BindDictionary(instance, dictionaryType, config, options);
return instance;
}
collectionInterface = FindOpenGenericInterface(typeof(IDictionary<,>), type);
if (collectionInterface != null)
{
object instance = Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(type.GenericTypeArguments[0], type.GenericTypeArguments[1]))!;
BindDictionary(instance, collectionInterface, config, options);
return instance;
}

which looks very similar to what FSharp do with printf specialization at runtime.
I can give only indirect hint on this in form of these declarations required to make Printf works in NativeAOT
https://github.com/kant2002/RdXmlLibrary/blob/625eaf01310f2e098b8a17cfe9658e140d37262e/FSharp.Core.xml#L25-L39

@agocke
Copy link
Member

agocke commented Jul 5, 2022

I assume from the ILC prefix that this is from NativeAOT -- moving back to the runtime repo.

@agocke agocke transferred this issue from dotnet/linker Jul 5, 2022
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jul 5, 2022
@ghost
Copy link

ghost commented Jul 5, 2022

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

Issue Details

Consider this small application

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;

var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var appSettings = GetAppSettings(config);
Console.WriteLine($"{appSettings.Database}: {appSettings.ConnectionString}");

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern",
    Justification = "AppSettings is consists only from primitive type and thus will never be trimmed")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:UnrecognizedReflectionPattern",
    Justification = "AppSettings is consists only from primitive type and thus will never be trimmed")]
AppSettings GetAppSettings(IConfiguration config)
{
    return config.Get<AppSettings>();
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public class AppSettings
{
    public string? ConnectionString { get; set; }

    public DatabaseServer Database { get; set; } = DatabaseServer.None;
}

public enum DatabaseServer
{
    None,
    SqlServer,
    PostgreSql,
    MySql
}

With following packages

dotnet add package Microsoft.Extensions.Configuration.Binder -v 7.0.0-*
dotnet add package Microsoft.Extensions.Configuration.Json -v 7.0.0-*

and `appsettings.json

{
    "ConnectionString": "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;SslMode=None;ConnectionReset=false;ConnectionIdlePingTime=900;ConnectionIdleTimeout=0;AutoEnlist=false;DefaultCommandTimeout=0;ConnectionTimeout=0;IgnorePrepare=false;",
    "Database": "mysql"
}

ILC spill warnings

ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.AttemptBindToCollectionInterfaces(Type,IConfiguration,BinderOptions): Using member 'System.Type.MakeGenericType(Type[]
)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.AttemptBindToCollectionInterfaces(Type,IConfiguration,BinderOptions): Using member 'System.Type.MakeGenericType(Type[]
)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.BindArray(Type,IEnumerable,IConfiguration,BinderOptions): Using member 'System.Array.CreateInstance(Type,Int32)' which
 has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The code for an array of the specified type might not be available. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
ILC : AOT analysis warning IL3050: Microsoft.Extensions.Configuration.ConfigurationBinder.BindToCollection(Type,IConfiguration,BinderOptions): Using member 'System.Type.MakeGenericType(Type[])' which has 'Req
uiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]
/_/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EnumConverter.cs(174): AOT analysis warning IL3050: System.ComponentModel.EnumConverter.ConvertTo(ITypeDescriptorContext,CultureI
nfo,Object,Type): Using member 'System.Enum.GetValues(Type)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. It might not be possible to create an array of the enum type a
t runtime. Use the GetValues<TEnum> overload instead. [D:\d\scratch\ConsoleApp7\ConsoleApp7.csproj]

From my understanding AppSettings is safe to map IConfiguration and I want express that. But as you can see all my silly attempts fails.

Author: kant2002
Assignees: -
Labels:

area-Extensions-Configuration

Milestone: -

@agocke
Copy link
Member

agocke commented Jul 5, 2022

I assume this is just because the APIs haven't been annotated with RequiresDynamicCode to bubble the requirements up to the public API, right?

@agocke agocke changed the title Cannot suppress configuration warning even if everything is ok. Microsoft.Extensions.Configuration APIs haven't been AOT-annotated Jul 5, 2022
@kant2002
Copy link
Contributor Author

kant2002 commented Jul 5, 2022

I assume from the ILC prefix that this is from NativeAOT -- moving back to the runtime repo.

You are right. I really misinterpret results and regular trimming works fine.

@eerhardt
Copy link
Member

eerhardt commented Jul 5, 2022

I assume this is just because the APIs haven't been annotated with RequiresDynamicCode

They have been annotated with RequiresUnreferencedCode already. Doesn't that imply that they are not safe for NativeAOT?

return config.Get();

This API is marked as RequiresUnreferencedCode:

[RequiresUnreferencedCode(TrimmingWarningMessage)]
public static T? Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this IConfiguration configuration)
=> configuration.Get<T>(_ => { });
/// <summary>
/// Attempts to bind the configuration instance to a new instance of type T.
/// If this configuration section has a value, that will be used.
/// Otherwise binding by matching property names against configuration keys recursively.
/// </summary>
/// <typeparam name="T">The type of the new instance to bind.</typeparam>
/// <param name="configuration">The configuration instance to bind.</param>
/// <param name="configureOptions">Configures the binder options.</param>
/// <returns>The new instance of T if successful, default(T) otherwise.</returns>
[RequiresUnreferencedCode(TrimmingWarningMessage)]
public static T? Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this IConfiguration configuration, Action<BinderOptions>? configureOptions)

@agocke
Copy link
Member

agocke commented Jul 5, 2022

Good question. So far we haven’t had RUC suppress RDC, but maybe we should @tlakollo @MichalStrehovsky

@tlakollo
Copy link
Contributor

tlakollo commented Jul 5, 2022

The way I see it is:
PublishTrimmed requires to be trim safe. Therefore any RequiresUnreferencedCode attribute makes the app not safe
PublishSingleFile requires to be single file safe. Therefore any RequiresAssemblyFiles attribute makes the app not safe
PublishAOT requires being trim, single file and dynamic code safe. Therefore any RequiresUnreferencedCode, RequiresAssemblyFiles, RequiresDynamicCode make the app not safe

They have been annotated with RequiresUnreferencedCode already. Doesn't that imply that they are not safe for NativeAOT?

RequiresUnreferencedCode means the result of the publishing the NativeAOT app is not safe, since trimming is not safe. Do we need to annotate also with RequiresDynamicCode because it might also be DynamicCode? The app is not going to be safe anyway. It's already trim unsafe and making it dynamic code unsafe won't make a difference for the final NativeAOT publishing.
On the other hand the attributes are independent of the publishing type, there could be a theoretical Publishing in the future that only requires being dynamic code safe and doesn't care about trimming or single file. In that case if you didn't annotate with RequiresDynamicCode you have a problem.

So far we haven’t had RUC suppress RDC, but maybe we should

My opinion as expresed before is that the attributes are independent, they even have different behaviors depending on the attribute (for example RequiresUnreferencedCode interacts with Reflection and DynamicallyAccessedMembers and the other attributes don't). They also can evolve and go completely different paths.
Also, it would be difficult to document, RUC does not suppress RAF, RDC does not suppress RUC and to be honest Im not sure RUC suppresses RDC. It kind of implies that all dynamic code is a trimming problem too, and again wouldnt hold up for a hipotetical scenario that only cares about dynamic code.

@MichalStrehovsky
Copy link
Member

Good question. So far we haven’t had RUC suppress RDC, but maybe we should @tlakollo @MichalStrehovsky

Two comments on that. Basically +1 on what Tlaka just wrote.

  1. It is true that we don't currently have a SKU that can't generate code at runtime and wouldn't be trimmed at the same time. Are we sure there won't be such SKU in the future?
  2. The message from RUC in this specific case is "In case the type is non-primitive, the trimmer cannot statically analyze the object's type so its members may be trimmed." and the user is suppressing the warning. The RDC warning from MakeGenericType is about a very different problem (we're trying to MakeGeneric a Dictionary). If you want to MakeGeneric a dictionary from double to float (both "primitive types") you'll run into a runtime exception. I think we'll want a RequiresDynamicCode that specifically describes the situations when MakeGeneric could be called and surface that to the user in addition to the trimming warning.

@eerhardt eerhardt added this to the 7.0.0 milestone Jul 12, 2022
@eerhardt eerhardt removed the untriaged New issue has not been triaged by the area owner label Jul 12, 2022
@eerhardt
Copy link
Member

It would be good to annotate these APIs for RequiresDynamicCode in 7.0.

@eerhardt eerhardt self-assigned this Aug 10, 2022
eerhardt added a commit to eerhardt/runtime that referenced this issue Aug 11, 2022
The only libraries that aren't enabled yet are DependencyInjection and Hosting. These will come in a separate PR.

Contributes to dotnet#71654
eerhardt added a commit that referenced this issue Aug 11, 2022
The only libraries that aren't enabled yet are DependencyInjection and Hosting. These will come in a separate PR.

Contributes to #71654
eerhardt added a commit to eerhardt/runtime that referenced this issue Aug 12, 2022
Using MEDI is annotated as RequiresDynamicCode because it supports enumerable and generic servcies with ValueTypes. When using DI with ValuesTypes, the array and generic code might not be available.

Contributes to dotnet#71654
eerhardt added a commit that referenced this issue Aug 12, 2022
Using MEDI is annotated as RequiresDynamicCode because it supports enumerable and generic servcies with ValueTypes. When using DI with ValuesTypes, the array and generic code might not be available.

Contributes to #71654
eerhardt added a commit to eerhardt/runtime that referenced this issue Aug 12, 2022
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Aug 12, 2022
eerhardt added a commit that referenced this issue Aug 13, 2022
* EnableAOTAnalyzer for Microsoft.Extensions.Hosting

Fix #71654

Plus clean up the interop in GetParentProcess.
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Aug 13, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants