Skip to content

Commit c79f02a

Browse files
github-actions[bot]agockeeerhardtstephentoub
authored
[release/8.0-rc1] Annotate System.Linq.Expressions with RequiresDynamicCode (#90616)
* 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 problem. * Remove suppression that's no longer needed * Fix tests for new feature switch behavior * Update src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs Co-authored-by: Eric Erhardt <[email protected]> * Typo in Throws call * Fix assert for net framework * Respond to PR comments * Add suppression * Typo * Update src/libraries/System.Linq.Expressions/src/Resources/Strings.resx Co-authored-by: Stephen Toub <[email protected]> * PR feedback --------- Co-authored-by: Andy Gocke <[email protected]> Co-authored-by: Andy Gocke <[email protected]> Co-authored-by: Eric Erhardt <[email protected]> Co-authored-by: Stephen Toub <[email protected]>
1 parent d29067b commit c79f02a

File tree

50 files changed

+468
-156
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+468
-156
lines changed

src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Generic;
55
using System.Linq;
6+
using System.Runtime.CompilerServices;
67
using System.Runtime.InteropServices;
78
using System.Threading;
89
using System.Threading.Tasks;
@@ -15,6 +16,26 @@ public static class AssertExtensions
1516
{
1617
private static bool IsNetFramework => RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework");
1718

19+
20+
/// <summary>
21+
/// Helper for AOT tests that verifies that the compile succeeds, or throws PlatformNotSupported
22+
/// when AOT is enabled.
23+
/// </summary>
24+
public static void ThrowsOnAot<T>(Action action)
25+
where T : Exception
26+
{
27+
#if NETCOREAPP // Dynamic code is always supported on .NET Framework
28+
if (!RuntimeFeature.IsDynamicCodeSupported)
29+
{
30+
Assert.Throws<T>(action);
31+
}
32+
else
33+
#endif
34+
{
35+
action();
36+
}
37+
}
38+
1839
public static void Throws<T>(Action action, string expectedMessage)
1940
where T : Exception
2041
{

src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,23 @@ static MethodInfo GetArrayEmptyMethodInfo(Type elementType)
123123
return ServiceLookupHelpers.GetArrayEmptyMethodInfo(elementType);
124124
}
125125

126+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
127+
Justification = "VerifyAotCompatibility ensures elementType is not a ValueType")]
128+
static NewArrayExpression NewArrayInit(Type elementType, IEnumerable<Expression> expr)
129+
{
130+
Debug.Assert(!ServiceProvider.VerifyAotCompatibility || !elementType.IsValueType, "VerifyAotCompatibility=true will throw during building the IEnumerableCallSite if elementType is a ValueType.");
131+
132+
return Expression.NewArrayInit(elementType, expr);
133+
}
134+
126135
if (callSite.ServiceCallSites.Length == 0)
127136
{
128137
return Expression.Constant(
129138
GetArrayEmptyMethodInfo(callSite.ItemType)
130139
.Invoke(obj: null, parameters: Array.Empty<object>()));
131140
}
132141

133-
return Expression.NewArrayInit(
142+
return NewArrayInit(
134143
callSite.ItemType,
135144
callSite.ServiceCallSites.Select(cs =>
136145
Convert(

src/libraries/System.Linq.Expressions/ref/System.Linq.Expressions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,9 +607,13 @@ protected Expression(System.Linq.Expressions.ExpressionType nodeType, System.Typ
607607
public static System.Linq.Expressions.NewExpression New(System.Reflection.ConstructorInfo constructor, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression>? arguments, params System.Reflection.MemberInfo[]? members) { throw null; }
608608
public static System.Linq.Expressions.NewExpression New(System.Reflection.ConstructorInfo constructor, params System.Linq.Expressions.Expression[]? arguments) { throw null; }
609609
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; }
610+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
610611
public static System.Linq.Expressions.NewArrayExpression NewArrayBounds(System.Type type, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression> bounds) { throw null; }
612+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
611613
public static System.Linq.Expressions.NewArrayExpression NewArrayBounds(System.Type type, params System.Linq.Expressions.Expression[] bounds) { throw null; }
614+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
612615
public static System.Linq.Expressions.NewArrayExpression NewArrayInit(System.Type type, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression> initializers) { throw null; }
616+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
613617
public static System.Linq.Expressions.NewArrayExpression NewArrayInit(System.Type type, params System.Linq.Expressions.Expression[] initializers) { throw null; }
614618
public static System.Linq.Expressions.UnaryExpression Not(System.Linq.Expressions.Expression expression) { throw null; }
615619
public static System.Linq.Expressions.UnaryExpression Not(System.Linq.Expressions.Expression expression, System.Reflection.MethodInfo? method) { throw null; }
@@ -1028,6 +1032,7 @@ internal MethodCallExpression() { }
10281032
System.Linq.Expressions.Expression System.Linq.Expressions.IArgumentProvider.GetArgument(int index) { throw null; }
10291033
public System.Linq.Expressions.MethodCallExpression Update(System.Linq.Expressions.Expression? @object, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression>? arguments) { throw null; }
10301034
}
1035+
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
10311036
public partial class NewArrayExpression : System.Linq.Expressions.Expression
10321037
{
10331038
internal NewArrayExpression() { }

src/libraries/System.Linq.Expressions/src/CompatibilitySuppressions.xml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,48 @@
9797
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
9898
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
9999
</Suppression>
100+
<Suppression>
101+
<DiagnosticId>CP0016</DiagnosticId>
102+
<Target>M:System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression,System.String,System.Type[],System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
103+
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
104+
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
105+
</Suppression>
106+
<Suppression>
107+
<DiagnosticId>CP0016</DiagnosticId>
108+
<Target>M:System.Linq.Expressions.Expression.Call(System.Type,System.String,System.Type[],System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
109+
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
110+
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
111+
</Suppression>
112+
<Suppression>
113+
<DiagnosticId>CP0016</DiagnosticId>
114+
<Target>M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Collections.Generic.IEnumerable{System.Linq.Expressions.Expression}):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
115+
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
116+
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
117+
</Suppression>
118+
<Suppression>
119+
<DiagnosticId>CP0016</DiagnosticId>
120+
<Target>M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
121+
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
122+
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
123+
</Suppression>
124+
<Suppression>
125+
<DiagnosticId>CP0016</DiagnosticId>
126+
<Target>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]</Target>
127+
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
128+
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
129+
</Suppression>
130+
<Suppression>
131+
<DiagnosticId>CP0016</DiagnosticId>
132+
<Target>M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Reflection.MethodInfo,System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
133+
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
134+
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
135+
</Suppression>
136+
<Suppression>
137+
<DiagnosticId>CP0016</DiagnosticId>
138+
<Target>M:System.Runtime.CompilerServices.CallSite`1.Create(System.Runtime.CompilerServices.CallSiteBinder):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
139+
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
140+
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
141+
</Suppression>
100142
<Suppression>
101143
<DiagnosticId>CP0020</DiagnosticId>
102144
<Target>M:System.Linq.Expressions.DynamicExpressionVisitor.#ctor</Target>

src/libraries/System.Linq.Expressions/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,4 +564,7 @@
564564
<data name="Arg_KeyNotFoundWithKey" xml:space="preserve">
565565
<value>The given key '{0}' was not present in the dictionary.</value>
566566
</data>
567+
<data name="LiftingInExpressionRequiresDynamicCode" xml:space="preserve">
568+
<value>Nullable lifting on non-primitive type '{0}' is only supported in expression trees when dynamic code generation is available.</value>
569+
</data>
567570
</root>

src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
44
<DefineConstants> $(DefineConstants);FEATURE_FAST_CREATE</DefineConstants>
55
<NoWarn>$(NoWarn);CA1859</NoWarn>
6-
<EnableAOTAnalyzer>false</EnableAOTAnalyzer>
76
<!--
87
Disable constant propagation so that methods referenced from ILLink.Substitutions.xml don't get inlined
98
with a wrong value at library build time and the substitution can still be selected at publish time.
@@ -91,6 +90,7 @@
9190
<Compile Include="System\Runtime\CompilerServices\CallSite.cs" />
9291
<Compile Include="System\Runtime\CompilerServices\CallSiteBinder.cs" />
9392
<Compile Include="System\Runtime\CompilerServices\CallSiteOps.cs" />
93+
<Compile Include="System\Runtime\CompilerServices\CallSiteOpsReflectionCache.cs" />
9494
<Compile Include="System\Runtime\CompilerServices\CallSiteHelpers.cs" />
9595
<Compile Include="System\Runtime\CompilerServices\DynamicAttribute.cs" />
9696
<Compile Include="System\Runtime\CompilerServices\DebugInfoGenerator.cs" />

src/libraries/System.Linq.Expressions/src/System/Dynamic/DynamicObject.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,7 @@ private DynamicMetaObject BuildCallMethodWithResult<TBinder>(MethodInfo method,
611611
Expression.Call(
612612
String_Format_String_ObjectArray,
613613
Expression.Constant(convertFailed),
614-
Expression.NewArrayInit(
615-
typeof(object),
614+
Expression.NewObjectArrayInit(
616615
new TrueReadOnlyCollection<Expression>(
617616
Expression.Condition(
618617
Expression.Equal(resultMO.Expression, AstUtils.Null),
@@ -645,7 +644,7 @@ private DynamicMetaObject BuildCallMethodWithResult<TBinder>(MethodInfo method,
645644
Expression.Block(
646645
new TrueReadOnlyCollection<ParameterExpression>(result, callArgs),
647646
new TrueReadOnlyCollection<Expression>(
648-
method != DynamicObject_TryBinaryOperation ? Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)) : Expression.Assign(callArgs, callArgsValue[0]),
647+
method != DynamicObject_TryBinaryOperation ? Expression.Assign(callArgs, Expression.NewObjectArrayInit(callArgsValue)) : Expression.Assign(callArgs, callArgsValue[0]),
649648
Expression.Condition(
650649
Expression.Call(
651650
GetLimitedSelf(),
@@ -705,7 +704,7 @@ private DynamicMetaObject CallMethodReturnLast<TBinder>(MethodInfo method, TBind
705704
Expression.Block(
706705
new TrueReadOnlyCollection<ParameterExpression>(result, callArgs),
707706
new TrueReadOnlyCollection<Expression>(
708-
Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)),
707+
Expression.Assign(callArgs, Expression.NewObjectArrayInit(callArgsValue)),
709708
Expression.Condition(
710709
Expression.Call(
711710
GetLimitedSelf(),
@@ -768,7 +767,7 @@ private DynamicMetaObject CallMethodNoResult<TBinder>(MethodInfo method, TBinder
768767
Expression.Block(
769768
new TrueReadOnlyCollection<ParameterExpression>(callArgs),
770769
new TrueReadOnlyCollection<Expression>(
771-
Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)),
770+
Expression.Assign(callArgs, Expression.NewObjectArrayInit(callArgsValue)),
772771
Expression.Condition(
773772
Expression.Call(
774773
GetLimitedSelf(),

src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/CachedReflectionInfo.cs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ internal static partial class CachedReflectionInfo
2121
public static MethodInfo CallSiteOps_SetNotMatched =>
2222
s_CallSiteOps_SetNotMatched ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.SetNotMatched))!;
2323

24-
private static MethodInfo? s_CallSiteOps_CreateMatchmaker;
25-
public static MethodInfo CallSiteOps_CreateMatchmaker =>
26-
s_CallSiteOps_CreateMatchmaker ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.CreateMatchmaker))!;
27-
2824
private static MethodInfo? s_CallSiteOps_GetMatch;
2925
public static MethodInfo CallSiteOps_GetMatch =>
3026
s_CallSiteOps_GetMatch ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetMatch))!;
@@ -33,34 +29,6 @@ internal static partial class CachedReflectionInfo
3329
public static MethodInfo CallSiteOps_ClearMatch =>
3430
s_CallSiteOps_ClearMatch ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.ClearMatch))!;
3531

36-
private static MethodInfo? s_CallSiteOps_UpdateRules;
37-
public static MethodInfo CallSiteOps_UpdateRules =>
38-
s_CallSiteOps_UpdateRules ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.UpdateRules))!;
39-
40-
private static MethodInfo? s_CallSiteOps_GetRules;
41-
public static MethodInfo CallSiteOps_GetRules =>
42-
s_CallSiteOps_GetRules ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetRules))!;
43-
44-
private static MethodInfo? s_CallSiteOps_GetRuleCache;
45-
public static MethodInfo CallSiteOps_GetRuleCache =>
46-
s_CallSiteOps_GetRuleCache ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetRuleCache))!;
47-
48-
private static MethodInfo? s_CallSiteOps_GetCachedRules;
49-
public static MethodInfo CallSiteOps_GetCachedRules =>
50-
s_CallSiteOps_GetCachedRules ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetCachedRules))!;
51-
52-
private static MethodInfo? s_CallSiteOps_AddRule;
53-
public static MethodInfo CallSiteOps_AddRule =>
54-
s_CallSiteOps_AddRule ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.AddRule))!;
55-
56-
private static MethodInfo? s_CallSiteOps_MoveRule;
57-
public static MethodInfo CallSiteOps_MoveRule =>
58-
s_CallSiteOps_MoveRule ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.MoveRule))!;
59-
60-
private static MethodInfo? s_CallSiteOps_Bind;
61-
public static MethodInfo CallSiteOps_Bind =>
62-
s_CallSiteOps_Bind ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.Bind))!;
63-
6432
private static MethodInfo? s_DynamicObject_TryGetMember;
6533
public static MethodInfo DynamicObject_TryGetMember =>
6634
s_DynamicObject_TryGetMember ??= typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryGetMember))!;

src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics.CodeAnalysis;
5+
using System.Linq.Expressions;
56
using System.Reflection;
67
using System.Reflection.Emit;
78
using System.Runtime.CompilerServices;
@@ -55,7 +56,10 @@ internal static Delegate CreateObjectArrayDelegate(Type delegateType, Func<objec
5556
{
5657
if (CanEmitObjectArrayDelegate)
5758
{
59+
#pragma warning disable IL3050
60+
// Suppress analyzer warnings since they don't currently support feature flags
5861
return CreateObjectArrayDelegateRefEmit(delegateType, handler);
62+
#pragma warning restore IL3050
5963
}
6064
else
6165
{
@@ -100,8 +104,7 @@ public static TReturn FuncThunk2<T1, T2, TReturn>(Func<object?[], object> handle
100104
return (TReturn)handler(new object?[]{t1, t2});
101105
}
102106

103-
private static MethodInfo GetEmptyObjectArrayMethod() =>
104-
typeof(Array).GetMethod(nameof(Array.Empty))!.MakeGenericMethod(typeof(object));
107+
private static MethodInfo GetEmptyObjectArrayMethod() => ((Func<object[]>)Array.Empty<object>).GetMethodInfo();
105108

106109
private static MethodInfo[] GetActionThunks()
107110
{
@@ -121,6 +124,7 @@ private static MethodInfo[] GetFuncThunks()
121124

122125
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
123126
Justification = "The above ActionThunk and FuncThunk methods don't have trimming annotations.")]
127+
[RequiresDynamicCode(Expression.GenericMethodRequiresDynamicCode)]
124128
private static MethodInfo? GetCSharpThunk(Type returnType, bool hasReturnValue, ParameterInfo[] parameters)
125129
{
126130
try
@@ -193,6 +197,7 @@ private static MethodInfo[] GetFuncThunks()
193197
// param0 = (T0)args[0]; // only generated for each byref argument
194198
// }
195199
// return (TRet)ret;
200+
[RequiresDynamicCode("Ref emit requires dynamic code.")]
196201
private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, Func<object?[], object?> handler)
197202
{
198203
if (!s_thunks.TryGetValue(delegateType, out MethodInfo? thunkMethod))

0 commit comments

Comments
 (0)