Skip to content

Commit 99bba64

Browse files
sbomermichaelgsharp
authored andcommitted
Preserve static type info for return value of ctor (dotnet#101212)
Instead of tracking the return value as "TopValue" or "unknown", this models the constructor as returning a value with a static type when called with newobj, letting us undo the workaround from dotnet#101031.
1 parent d9e7f2a commit 99bba64

File tree

23 files changed

+129
-55
lines changed

23 files changed

+129
-55
lines changed

src/coreclr/tools/Common/Compiler/Dataflow/MethodProxy.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ internal partial ImmutableArray<GenericParameterProxy> GetGenericParameters()
6666
return builder.ToImmutableArray();
6767
}
6868

69+
internal partial bool IsConstructor() => Method.IsConstructor;
70+
6971
internal partial bool IsStatic() => Method.Signature.IsStatic;
7072

7173
internal partial bool HasImplicitThis() => !Method.Signature.IsStatic;

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -951,12 +951,12 @@ internal partial bool MethodRequiresDataFlowAnalysis(MethodProxy method)
951951
=> RequiresDataflowAnalysisDueToSignature(method.Method);
952952

953953
#pragma warning disable CA1822 // Other partial implementations are not in the ilc project
954-
internal partial MethodReturnValue GetMethodReturnValue(MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
954+
internal partial MethodReturnValue GetMethodReturnValue(MethodProxy method, bool isNewObj, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
955955
#pragma warning restore CA1822 // Mark members as static
956-
=> new MethodReturnValue(method.Method, dynamicallyAccessedMemberTypes);
956+
=> new MethodReturnValue(method.Method, isNewObj, dynamicallyAccessedMemberTypes);
957957

958-
internal partial MethodReturnValue GetMethodReturnValue(MethodProxy method)
959-
=> GetMethodReturnValue(method, GetReturnParameterAnnotation(method.Method));
958+
internal partial MethodReturnValue GetMethodReturnValue(MethodProxy method, bool isNewObj)
959+
=> GetMethodReturnValue(method, isNewObj, GetReturnParameterAnnotation(method.Method));
960960

961961
internal partial GenericParameterValue GetGenericParameterValue(GenericParameterProxy genericParameter)
962962
=> new GenericParameterValue(genericParameter.GenericParameter, GetGenericParameterAnnotation(genericParameter.GenericParameter));

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public HandleCallAction(
3939
{
4040
_reflectionMarker = reflectionMarker;
4141
_operation = operation;
42+
_isNewObj = operation == ILOpcode.newobj;
4243
_diagnosticContext = diagnosticContext;
4344
_callingMethod = callingMethod;
4445
_annotations = annotations;
@@ -337,12 +338,6 @@ private partial bool TryHandleIntrinsic (
337338
if (Intrinsics.GetIntrinsicIdForMethod(_callingMethod) == IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo)
338339
break;
339340

340-
if (param.IsEmpty())
341-
{
342-
// The static value is unknown and the below `foreach` won't execute
343-
_reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ReflectedDelegate(null), "Delegate.Method access on unknown delegate type");
344-
}
345-
346341
foreach (var valueNode in param.AsEnumerable())
347342
{
348343
TypeDesc? staticType = (valueNode as IValueWithStaticType)?.StaticType?.Type;
@@ -390,7 +385,7 @@ private partial bool TryHandleIntrinsic (
390385
if (staticType is null || (!staticType.IsDefType && !staticType.IsArray))
391386
{
392387
// We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations"
393-
AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod));
388+
AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj));
394389
}
395390
else if (staticType.IsSealed() || staticType.IsTypeOf("System", "Delegate"))
396391
{
@@ -425,7 +420,7 @@ private partial bool TryHandleIntrinsic (
425420
// Return a value which is "unknown type" with annotation. For now we'll use the return value node
426421
// for the method, which means we're loosing the information about which staticType this
427422
// started with. For now we don't need it, but we can add it later on.
428-
AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, annotation));
423+
AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj, annotation));
429424
}
430425
}
431426
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodReturnValue.cs

Lines changed: 4 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.Collections.Generic;
5+
using System.Diagnostics;
56
using System.Diagnostics.CodeAnalysis;
67
using ILCompiler.Dataflow;
78
using ILLink.Shared.DataFlow;
@@ -16,9 +17,10 @@ namespace ILLink.Shared.TrimAnalysis
1617
/// </summary>
1718
internal partial record MethodReturnValue
1819
{
19-
public MethodReturnValue(MethodDesc method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
20+
public MethodReturnValue(MethodDesc method, bool isNewObj, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
2021
{
21-
StaticType = method.Signature.ReturnType;
22+
Debug.Assert(!isNewObj || method.IsConstructor, "isNewObj can only be true for constructors");
23+
StaticType = isNewObj ? method.OwningType : method.Signature.ReturnType;
2224
Method = method;
2325
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
2426
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ protected override void Scan(MethodIL methodBody, ref InterproceduralState inter
118118
if (!methodBody.OwningMethod.Signature.ReturnType.IsVoid)
119119
{
120120
var method = methodBody.OwningMethod;
121-
var methodReturnValue = _annotations.GetMethodReturnValue(method);
121+
var methodReturnValue = _annotations.GetMethodReturnValue(method, isNewObj: false);
122122
if (methodReturnValue.DynamicallyAccessedMemberTypes != 0)
123123
HandleAssignmentPattern(_origin, ReturnValue, methodReturnValue, method.GetDisplayName());
124124
}
@@ -315,7 +315,8 @@ public static MultiValue HandleCall(
315315
var callingMethodDefinition = callingMethodBody.OwningMethod;
316316
Debug.Assert(callingMethodDefinition == diagnosticContext.Origin.MemberDefinition);
317317

318-
var annotatedMethodReturnValue = reflectionMarker.Annotations.GetMethodReturnValue(calledMethod);
318+
bool isNewObj = operation == ILOpcode.newobj;
319+
var annotatedMethodReturnValue = reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, isNewObj);
319320
Debug.Assert(
320321
RequiresReflectionMethodBodyScannerForCallSite(reflectionMarker.Annotations, calledMethod) ||
321322
annotatedMethodReturnValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.None);

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/FlowAnnotations.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ public static DynamicallyAccessedMemberTypes GetTypeAnnotation(ITypeSymbol type)
103103
internal partial bool MethodRequiresDataFlowAnalysis (MethodProxy method)
104104
=> RequiresDataFlowAnalysis (method.Method);
105105

106-
internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
107-
=> new MethodReturnValue (method.Method, dynamicallyAccessedMemberTypes);
106+
internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method, bool isNewObj, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
107+
=> new MethodReturnValue (method.Method, isNewObj, dynamicallyAccessedMemberTypes);
108108

109-
internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method)
110-
=> GetMethodReturnValue (method, GetMethodReturnValueAnnotation (method.Method));
109+
internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method, bool isNewObj)
110+
=> GetMethodReturnValue (method, isNewObj, GetMethodReturnValueAnnotation (method.Method));
111111

112112
internal partial GenericParameterValue GetGenericParameterValue (GenericParameterProxy genericParameter)
113113
=> new GenericParameterValue (genericParameter.TypeParameterSymbol);

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/HandleCallAction.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public HandleCallAction (
3333
{
3434
_owningSymbol = owningSymbol;
3535
_operation = operation;
36+
_isNewObj = operation.Kind == OperationKind.ObjectCreation;
3637
_diagnosticContext = diagnosticContext;
3738
_annotations = FlowAnnotations.Instance;
3839
_reflectionAccessAnalyzer = default;
@@ -86,7 +87,7 @@ private partial bool TryHandleIntrinsic (
8687

8788
if (staticType is null) {
8889
// We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations"
89-
AddReturnValue (FlowAnnotations.Instance.GetMethodReturnValue (calledMethod));
90+
AddReturnValue (FlowAnnotations.Instance.GetMethodReturnValue (calledMethod, _isNewObj));
9091
} else if (staticType.IsSealed || staticType.IsTypeOf ("System", "Delegate") || staticType.TypeKind == TypeKind.Array) {
9192
// We can treat this one the same as if it was a typeof() expression
9293

@@ -106,7 +107,7 @@ private partial bool TryHandleIntrinsic (
106107
AddReturnValue (new SystemTypeValue (new (staticType)));
107108
} else {
108109
var annotation = FlowAnnotations.GetTypeAnnotation (staticType);
109-
AddReturnValue (FlowAnnotations.Instance.GetMethodReturnValue (calledMethod, annotation));
110+
AddReturnValue (FlowAnnotations.Instance.GetMethodReturnValue (calledMethod, _isNewObj, annotation));
110111
}
111112
}
112113
break;

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodProxy.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ internal partial ImmutableArray<GenericParameterProxy> GetGenericParameters ()
4646
return builder.ToImmutableArray ();
4747
}
4848

49+
internal partial bool IsConstructor () => Method.IsConstructor ();
50+
4951
internal partial bool IsStatic () => Method.IsStatic;
5052

5153
internal partial bool HasImplicitThis () => Method.HasImplicitThis ();

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/MethodReturnValue.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics;
56
using System.Diagnostics.CodeAnalysis;
67
using ILLink.RoslynAnalyzer;
78
using ILLink.Shared.DataFlow;
@@ -11,16 +12,17 @@ namespace ILLink.Shared.TrimAnalysis
1112
{
1213
internal partial record MethodReturnValue
1314
{
14-
public MethodReturnValue (IMethodSymbol methodSymbol)
15-
: this (methodSymbol, FlowAnnotations.GetMethodReturnValueAnnotation (methodSymbol))
15+
public MethodReturnValue (IMethodSymbol methodSymbol, bool isNewObj)
16+
: this (methodSymbol, isNewObj, FlowAnnotations.GetMethodReturnValueAnnotation (methodSymbol))
1617
{
1718
}
1819

19-
public MethodReturnValue (IMethodSymbol methodSymbol, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
20+
public MethodReturnValue (IMethodSymbol methodSymbol, bool isNewObj, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
2021
{
22+
Debug.Assert (!isNewObj || methodSymbol.MethodKind == MethodKind.Constructor, "isNewObj can only be true for constructors");
2123
MethodSymbol = methodSymbol;
2224
DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes;
23-
StaticType = new (methodSymbol.ReturnType);
25+
StaticType = new (isNewObj ? methodSymbol.ContainingType : methodSymbol.ReturnType);
2426
}
2527

2628
public readonly IMethodSymbol MethodSymbol;

src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/TrimAnalysisVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public override MultiValue VisitConversion (IConversionOperation operation, Stat
116116
var value = base.VisitConversion (operation, state);
117117

118118
if (operation.OperatorMethod != null)
119-
return operation.OperatorMethod.ReturnType.IsTypeInterestingForDataflow () ? new MethodReturnValue (operation.OperatorMethod) : value;
119+
return operation.OperatorMethod.ReturnType.IsTypeInterestingForDataflow () ? new MethodReturnValue (operation.OperatorMethod, isNewObj: false) : value;
120120

121121
// TODO - is it possible to have annotation on the operator method parameters?
122122
// if so, will these be checked here?
@@ -341,7 +341,7 @@ public override void HandleReturnValue (MultiValue returnValue, IOperation opera
341341
return;
342342

343343
if (method.ReturnType.IsTypeInterestingForDataflow ()) {
344-
var returnParameter = new MethodReturnValue (method);
344+
var returnParameter = new MethodReturnValue (method, isNewObj: false);
345345

346346
TrimAnalysisPatterns.Add (
347347
new TrimAnalysisAssignmentPattern (returnValue, returnParameter, operation, OwningSymbol, featureContext),

0 commit comments

Comments
 (0)