From 067a3572a555f1cdef7ca29a54efb7b38cfa086f Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Mon, 14 Aug 2023 19:45:30 -0700 Subject: [PATCH] Annotate System.Linq.Expressions with RequiresDynamicCode (#90456) * Annotate System.Linq.Expressions with RequiresDynamicCode All this ended up with an RUC on Expression.Compile due to new arrays. I could potentially silence this warning with a feature flag, but it is a real risk, and one that users could maybe work around if alterted to the Co-authored-by: Eric Erhardt Co-authored-by: Stephen Toub --- .../TestUtilities/System/AssertExtensions.cs | 21 +++++++ .../Expressions/ExpressionResolverBuilder.cs | 11 +++- .../ref/System.Linq.Expressions.cs | 5 ++ .../src/CompatibilitySuppressions.xml | 42 +++++++++++++ .../src/Resources/Strings.resx | 3 + .../src/System.Linq.Expressions.csproj | 2 +- .../src/System/Dynamic/DynamicObject.cs | 9 ++- .../Dynamic/Utils/CachedReflectionInfo.cs | 32 ---------- .../System/Dynamic/Utils/DelegateHelpers.cs | 9 ++- .../src/System/Dynamic/Utils/TypeUtils.cs | 62 +++++++++++++++++++ .../Linq/Expressions/BinaryExpression.cs | 8 ++- .../Linq/Expressions/Compiler/AssemblyGen.cs | 1 + .../Compiler/CompilerScope.Storage.cs | 3 + .../Expressions/Compiler/CompilerScope.cs | 3 + .../Expressions/Compiler/DelegateHelpers.cs | 3 + .../Compiler/LambdaCompiler.Binary.cs | 3 +- .../Expressions/Compiler/LambdaCompiler.cs | 2 + .../Linq/Expressions/Compiler/StackSpiller.cs | 1 + .../Expressions/Compiler/VariableBinder.cs | 1 + .../src/System/Linq/Expressions/Expression.cs | 5 ++ .../Interpreter/ArrayOperations.cs | 5 ++ .../Interpreter/InstructionList.cs | 9 +-- .../Expressions/Interpreter/LightCompiler.cs | 4 ++ .../Linq/Expressions/LambdaExpression.cs | 11 ++++ .../Linq/Expressions/ListInitExpression.cs | 4 ++ .../Linq/Expressions/MethodCallExpression.cs | 4 ++ .../Linq/Expressions/NewArrayExpression.cs | 15 +++++ .../src/System/Linq/Expressions/Strings.cs | 8 +++ .../Linq/Expressions/UnaryExpression.cs | 4 +- .../Runtime/CompilerServices/CallSite.cs | 53 ++++++++++++---- .../CompilerServices/CallSiteBinder.cs | 4 +- .../Runtime/CompilerServices/CallSiteOps.cs | 3 + .../CallSiteOpsReflectionCache.cs | 37 +++++++++++ .../Arithmetic/BinaryMultiplyTests.cs | 38 +++++++----- .../tests/HelperTypes.cs | 2 + .../Lifted/LiftedAddCheckedNullableTests.cs | 13 ++-- .../tests/Lifted/LiftedAddNullableTests.cs | 13 ++-- .../Lifted/LiftedBitwiseAndNullableTests.cs | 13 ++-- .../LiftedBitwiseExclusiveOrNullableTests.cs | 13 ++-- .../Lifted/LiftedBitwiseOrNullableTests.cs | 13 ++-- .../tests/Lifted/LiftedDivideNullableTests.cs | 13 ++-- .../tests/Lifted/LiftedModuloNullableTests.cs | 13 ++-- .../LiftedMultiplyCheckedNullableTests.cs | 13 ++-- .../Lifted/LiftedMultiplyNullableTests.cs | 13 ++-- .../LiftedSubtractCheckedNullableTests.cs | 13 ++-- .../Lifted/LiftedSubtractNullableTests.cs | 13 ++-- .../tests/SequenceTests/SequenceTests.cs | 12 ++-- .../Unary/UnaryDecrementNullableTests.cs | 20 +++--- .../tests/Unary/UnaryDecrementTests.cs | 2 +- .../Unary/UnaryIncrementNullableTests.cs | 20 +++--- 50 files changed, 468 insertions(+), 156 deletions(-) create mode 100644 src/libraries/System.Linq.Expressions/src/System/Runtime/CompilerServices/CallSiteOpsReflectionCache.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs index bf676b7b6f26b..230e4cb4276a4 100644 --- a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs +++ b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -15,6 +16,26 @@ public static class AssertExtensions { private static bool IsNetFramework => RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework"); + + /// + /// Helper for AOT tests that verifies that the compile succeeds, or throws PlatformNotSupported + /// when AOT is enabled. + /// + public static void ThrowsOnAot(Action action) + where T : Exception + { +#if NETCOREAPP // Dynamic code is always supported on .NET Framework + if (!RuntimeFeature.IsDynamicCodeSupported) + { + Assert.Throws(action); + } + else +#endif + { + action(); + } + } + public static void Throws(Action action, string expectedMessage) where T : Exception { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs index c28b9f00bb4e7..670c62ee2f69a 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs @@ -123,6 +123,15 @@ static MethodInfo GetArrayEmptyMethodInfo(Type elementType) return ServiceLookupHelpers.GetArrayEmptyMethodInfo(elementType); } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "VerifyAotCompatibility ensures elementType is not a ValueType")] + static NewArrayExpression NewArrayInit(Type elementType, IEnumerable expr) + { + Debug.Assert(!ServiceProvider.VerifyAotCompatibility || !elementType.IsValueType, "VerifyAotCompatibility=true will throw during building the IEnumerableCallSite if elementType is a ValueType."); + + return Expression.NewArrayInit(elementType, expr); + } + if (callSite.ServiceCallSites.Length == 0) { return Expression.Constant( @@ -130,7 +139,7 @@ static MethodInfo GetArrayEmptyMethodInfo(Type elementType) .Invoke(obj: null, parameters: Array.Empty())); } - return Expression.NewArrayInit( + return NewArrayInit( callSite.ItemType, callSite.ServiceCallSites.Select(cs => Convert( diff --git a/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs b/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs index 98bfd460ddb96..015cfb079d360 100644 --- a/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs +++ b/src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs @@ -607,9 +607,13 @@ protected Expression(System.Linq.Expressions.ExpressionType nodeType, System.Typ public static System.Linq.Expressions.NewExpression New(System.Reflection.ConstructorInfo constructor, System.Collections.Generic.IEnumerable? arguments, params System.Reflection.MemberInfo[]? members) { throw null; } public static System.Linq.Expressions.NewExpression New(System.Reflection.ConstructorInfo constructor, params System.Linq.Expressions.Expression[]? arguments) { throw null; } public static System.Linq.Expressions.NewExpression New([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")] public static System.Linq.Expressions.NewArrayExpression NewArrayBounds(System.Type type, System.Collections.Generic.IEnumerable bounds) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")] public static System.Linq.Expressions.NewArrayExpression NewArrayBounds(System.Type type, params System.Linq.Expressions.Expression[] bounds) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")] public static System.Linq.Expressions.NewArrayExpression NewArrayInit(System.Type type, System.Collections.Generic.IEnumerable initializers) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")] public static System.Linq.Expressions.NewArrayExpression NewArrayInit(System.Type type, params System.Linq.Expressions.Expression[] initializers) { throw null; } public static System.Linq.Expressions.UnaryExpression Not(System.Linq.Expressions.Expression expression) { throw null; } public static System.Linq.Expressions.UnaryExpression Not(System.Linq.Expressions.Expression expression, System.Reflection.MethodInfo? method) { throw null; } @@ -1028,6 +1032,7 @@ internal MethodCallExpression() { } System.Linq.Expressions.Expression System.Linq.Expressions.IArgumentProvider.GetArgument(int index) { throw null; } public System.Linq.Expressions.MethodCallExpression Update(System.Linq.Expressions.Expression? @object, System.Collections.Generic.IEnumerable? arguments) { throw null; } } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")] public partial class NewArrayExpression : System.Linq.Expressions.Expression { internal NewArrayExpression() { } diff --git a/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml b/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml index 8185aa8209fa1..bf51da8ccbe29 100644 --- a/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml @@ -97,6 +97,48 @@ ref/net8.0/System.Linq.Expressions.dll lib/net8.0/System.Linq.Expressions.dll + + CP0016 + M:System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression,System.String,System.Type[],System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute] + ref/net8.0/System.Linq.Expressions.dll + lib/net8.0/System.Linq.Expressions.dll + + + CP0016 + M:System.Linq.Expressions.Expression.Call(System.Type,System.String,System.Type[],System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute] + ref/net8.0/System.Linq.Expressions.dll + lib/net8.0/System.Linq.Expressions.dll + + + CP0016 + M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Collections.Generic.IEnumerable{System.Linq.Expressions.Expression}):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute] + ref/net8.0/System.Linq.Expressions.dll + lib/net8.0/System.Linq.Expressions.dll + + + CP0016 + M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute] + ref/net8.0/System.Linq.Expressions.dll + lib/net8.0/System.Linq.Expressions.dll + + + CP0016 + M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Reflection.MethodInfo,System.Collections.Generic.IEnumerable{System.Linq.Expressions.Expression}):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute] + ref/net8.0/System.Linq.Expressions.dll + lib/net8.0/System.Linq.Expressions.dll + + + CP0016 + M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Reflection.MethodInfo,System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute] + ref/net8.0/System.Linq.Expressions.dll + lib/net8.0/System.Linq.Expressions.dll + + + CP0016 + M:System.Runtime.CompilerServices.CallSite`1.Create(System.Runtime.CompilerServices.CallSiteBinder):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute] + ref/net8.0/System.Linq.Expressions.dll + lib/net8.0/System.Linq.Expressions.dll + CP0020 M:System.Linq.Expressions.DynamicExpressionVisitor.#ctor diff --git a/src/libraries/System.Linq.Expressions/src/Resources/Strings.resx b/src/libraries/System.Linq.Expressions/src/Resources/Strings.resx index 01f6ec7be8ddc..cfbf09d82e02e 100644 --- a/src/libraries/System.Linq.Expressions/src/Resources/Strings.resx +++ b/src/libraries/System.Linq.Expressions/src/Resources/Strings.resx @@ -564,4 +564,7 @@ The given key '{0}' was not present in the dictionary. + + Nullable lifting on non-primitive type '{0}' is only supported in expression trees when dynamic code generation is available. + diff --git a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj index 47375e1d82779..08f93036a937d 100644 --- a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj +++ b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj @@ -3,7 +3,6 @@ $(NetCoreAppCurrent) $(DefineConstants);FEATURE_FAST_CREATE $(NoWarn);CA1859 - false