Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJackson Schuster <36744439+jtschuster@users.noreply.github.com>2022-08-12 21:22:45 +0300
committerGitHub <noreply@github.com>2022-08-12 21:22:45 +0300
commit1ef0acf496369c15911dd5fe78e2d100659fa389 (patch)
tree0cfd25aef8d94fa95cf6530fe255bca91f2c012d
parentd559347987ba45c0dc3389cb96dce1ae20204513 (diff)
Dataflow support for ref fields (#2947)
Adds support and basic tests for ref fields. The tests ensure that a value written to an ref field is properly annotated, or an address written to a ref field has a properly annotated value, but there are some holes that can be created by assigning the address of a local or over-annotated value to an annotated ref field.
-rw-r--r--src/linker/Linker.Dataflow/MethodBodyScanner.cs33
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs43
-rw-r--r--test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs6
-rw-r--r--test/Mono.Linker.Tests.Cases/Basic/UnusedFieldsOfStructsAreKept.cs41
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/RefFieldDataFlow.cs560
5 files changed, 649 insertions, 34 deletions
diff --git a/src/linker/Linker.Dataflow/MethodBodyScanner.cs b/src/linker/Linker.Dataflow/MethodBodyScanner.cs
index 1c056663e..3233b917c 100644
--- a/src/linker/Linker.Dataflow/MethodBodyScanner.cs
+++ b/src/linker/Linker.Dataflow/MethodBodyScanner.cs
@@ -542,7 +542,7 @@ namespace Mono.Linker.Dataflow
case Code.Stfld:
case Code.Stsfld:
- ScanStfld (operation, currentStack, thisMethod, methodBody, ref interproceduralState);
+ ScanStfld (operation, currentStack, thisMethod, methodBody, locals, ref interproceduralState);
break;
case Code.Cpobj:
@@ -558,7 +558,7 @@ namespace Mono.Linker.Dataflow
case Code.Stind_R8:
case Code.Stind_Ref:
case Code.Stobj:
- ScanIndirectStore (operation, currentStack, methodBody, locals, curBasicBlock);
+ ScanIndirectStore (operation, currentStack, methodBody, locals, curBasicBlock, ref interproceduralState);
break;
case Code.Initobj:
@@ -863,12 +863,13 @@ namespace Mono.Linker.Dataflow
Stack<StackSlot> currentStack,
MethodBody methodBody,
LocalVariableStore locals,
- int curBasicBlock)
+ int curBasicBlock,
+ ref InterproceduralState ipState)
{
StackSlot valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset);
StackSlot destination = PopUnknown (currentStack, 1, methodBody, operation.Offset);
- StoreInReference (destination.Value, valueToStore.Value, methodBody.Method, operation, locals, curBasicBlock);
+ StoreInReference (destination.Value, valueToStore.Value, methodBody.Method, operation, locals, curBasicBlock, ref ipState);
}
/// <summary>
@@ -879,7 +880,7 @@ namespace Mono.Linker.Dataflow
/// <param name="method">The method body that contains the operation causing the store</param>
/// <param name="operation">The instruction causing the store</param>
/// <exception cref="LinkerFatalErrorException">Throws if <paramref name="target"/> is not a valid target for an indirect store.</exception>
- protected void StoreInReference (MultiValue target, MultiValue source, MethodDefinition method, Instruction operation, LocalVariableStore locals, int curBasicBlock)
+ protected void StoreInReference (MultiValue target, MultiValue source, MethodDefinition method, Instruction operation, LocalVariableStore locals, int curBasicBlock, ref InterproceduralState ipState)
{
foreach (var value in target) {
switch (value) {
@@ -902,6 +903,9 @@ namespace Mono.Linker.Dataflow
// Ref returns don't have special ReferenceValue values, so assume if the target here is a MethodReturnValue then it must be a ref return value
HandleStoreMethodReturnValue (method, methodReturnValue, operation, source);
break;
+ case FieldValue fieldValue:
+ HandleStoreField (method, fieldValue, operation, DereferenceValue (source, locals, ref ipState));
+ break;
case IValueWithStaticType valueWithStaticType:
if (valueWithStaticType.StaticType is not null && _context.Annotations.FlowAnnotations.IsTypeInterestingForDataflow (valueWithStaticType.StaticType))
throw new LinkerFatalErrorException (MessageContainer.CreateErrorMessage (
@@ -971,6 +975,7 @@ namespace Mono.Linker.Dataflow
Stack<StackSlot> currentStack,
MethodDefinition thisMethod,
MethodBody methodBody,
+ LocalVariableStore locals,
ref InterproceduralState interproceduralState)
{
StackSlot valueToStoreSlot = PopUnknown (currentStack, 1, methodBody, operation.Offset);
@@ -990,7 +995,10 @@ namespace Mono.Linker.Dataflow
if (value is not FieldValue fieldValue)
continue;
- HandleStoreField (thisMethod, fieldValue, operation, valueToStoreSlot.Value);
+ // Incomplete handling of ref fields -- if we're storing a reference to a value, pretend it's just the value
+ MultiValue valueToStore = DereferenceValue (valueToStoreSlot.Value, locals, ref interproceduralState);
+
+ HandleStoreField (thisMethod, fieldValue, operation, valueToStore);
}
}
}
@@ -1034,7 +1042,7 @@ namespace Mono.Linker.Dataflow
return methodParams;
}
- internal MultiValue DereferenceValue (MultiValue maybeReferenceValue, Dictionary<VariableDefinition, ValueBasicBlockPair> locals, ref InterproceduralState interproceduralState)
+ internal MultiValue DereferenceValue (MultiValue maybeReferenceValue, LocalVariableStore locals, ref InterproceduralState interproceduralState)
{
MultiValue dereferencedValue = MultiValueLattice.Top;
foreach (var value in maybeReferenceValue) {
@@ -1059,6 +1067,10 @@ namespace Mono.Linker.Dataflow
break;
case ReferenceValue referenceValue:
throw new NotImplementedException ($"Unhandled dereference of ReferenceValue of type {referenceValue.GetType ().FullName}");
+ // Incomplete handling for ref values
+ case FieldValue fieldValue:
+ dereferencedValue = MultiValue.Meet (dereferencedValue, fieldValue);
+ break;
default:
dereferencedValue = MultiValue.Meet (dereferencedValue, value);
break;
@@ -1076,7 +1088,8 @@ namespace Mono.Linker.Dataflow
ValueNodeList methodArguments,
Instruction operation,
LocalVariableStore locals,
- int curBasicBlock)
+ int curBasicBlock,
+ ref InterproceduralState ipState)
{
MethodDefinition? calledMethodDefinition = _context.Resolve (calledMethod);
bool methodIsResolved = calledMethodDefinition is not null;
@@ -1088,7 +1101,7 @@ namespace Mono.Linker.Dataflow
SingleValue newByRefValue = methodIsResolved
? _context.Annotations.FlowAnnotations.GetMethodParameterValue (calledMethodDefinition!, parameterIndex)
: UnknownValue.Instance;
- StoreInReference (methodArguments[ilArgumentIndex], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock);
+ StoreInReference (methodArguments[ilArgumentIndex], newByRefValue, callingMethodBody.Method, operation, locals, curBasicBlock, ref ipState);
}
}
@@ -1135,7 +1148,7 @@ namespace Mono.Linker.Dataflow
if (isNewObj || !calledMethod.ReturnsVoid ())
currentStack.Push (new StackSlot (methodReturnValue));
- AssignRefAndOutParameters (callingMethodBody, calledMethod, methodArguments, operation, locals, curBasicBlock);
+ AssignRefAndOutParameters (callingMethodBody, calledMethod, methodArguments, operation, locals, curBasicBlock, ref interproceduralState);
foreach (var param in methodArguments) {
foreach (var v in param) {
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index ba5798d81..66733c12a 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -3503,18 +3503,8 @@ namespace Mono.Linker.Steps
foreach (Instruction instruction in body.Instructions) {
switch (instruction.OpCode.OperandType) {
case OperandType.InlineField:
- switch (instruction.OpCode.Code) {
- case Code.Stfld:
- case Code.Stsfld:
- case Code.Ldflda:
- case Code.Ldsflda:
- if (ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand))
- return true;
- break;
-
- default:
- break;
- }
+ if (InstructionRequiresReflectionMethodBodyScannerForFieldAccess (instruction))
+ return true;
break;
case OperandType.InlineMethod:
@@ -3595,22 +3585,27 @@ namespace Mono.Linker.Steps
MarkInterfaceImplementation (implementation, new MessageOrigin (type));
}
+ bool InstructionRequiresReflectionMethodBodyScannerForFieldAccess (Instruction instruction)
+ => instruction.OpCode.Code switch {
+ // Field stores (Storing value to annotated field must be checked)
+ Code.Stfld or
+ Code.Stsfld or
+ // Field address loads (as those can be used to store values to annotated field and thus must be checked)
+ Code.Ldflda or
+ Code.Ldsflda
+ => ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand),
+ // For ref fields, ldfld loads an address which can be used to store values to annotated fields
+ Code.Ldfld or Code.Ldsfld when ((FieldReference) instruction.Operand).FieldType.IsByRefOrPointer ()
+ => ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand),
+ // Other field operations are not interesting as they don't need to be checked
+ _ => false
+ };
+
protected virtual void MarkInstruction (Instruction instruction, MethodDefinition method, ref bool requiresReflectionMethodBodyScanner)
{
switch (instruction.OpCode.OperandType) {
case OperandType.InlineField:
- switch (instruction.OpCode.Code) {
- case Code.Stfld: // Field stores (Storing value to annotated field must be checked)
- case Code.Stsfld:
- case Code.Ldflda: // Field address loads (as those can be used to store values to annotated field and thus must be checked)
- case Code.Ldsflda:
- requiresReflectionMethodBodyScanner |=
- ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (Context, (FieldReference) instruction.Operand);
- break;
-
- default: // Other field operations are not interesting as they don't need to be checked
- break;
- }
+ requiresReflectionMethodBodyScanner |= InstructionRequiresReflectionMethodBodyScannerForFieldAccess (instruction);
ScopeStack.UpdateCurrentScopeInstructionOffset (instruction.Offset);
MarkField ((FieldReference) instruction.Operand, new DependencyInfo (DependencyKind.FieldAccess, method), ScopeStack.CurrentScope.Origin);
diff --git a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
index 020d3f7ca..e622c02e0 100644
--- a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
+++ b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
@@ -253,6 +253,12 @@ namespace ILLink.RoslynAnalyzer.Tests
return RunTest (nameof (PropertyDataFlow));
}
+ [Fact]
+ public Task RefFieldDataFlow ()
+ {
+ return RunTest (nameof (RefFieldDataFlow));
+ }
+
[Fact (Skip = "https://github.com/dotnet/linker/issues/2273")]
public Task SuppressWarningWithLinkAttributes ()
{
diff --git a/test/Mono.Linker.Tests.Cases/Basic/UnusedFieldsOfStructsAreKept.cs b/test/Mono.Linker.Tests.Cases/Basic/UnusedFieldsOfStructsAreKept.cs
index ff4bb5ae9..3d463eb11 100644
--- a/test/Mono.Linker.Tests.Cases/Basic/UnusedFieldsOfStructsAreKept.cs
+++ b/test/Mono.Linker.Tests.Cases/Basic/UnusedFieldsOfStructsAreKept.cs
@@ -40,10 +40,51 @@ namespace Mono.Linker.Tests.Cases.Basic
public ref int UnusedRefField;
[Kept]
+ public ref ReferencedType UnusedClass;
+
+ [Kept]
+ public ref ReferencedStruct UnusedStruct;
+
+ [Kept]
+ public ReferencedRefStruct UnusedRefStruct;
+
+ [Kept]
int UnusedField;
[Kept]
int UsedField;
}
+
+ [Kept]
+ struct ReferencedStruct
+ {
+ [Kept]
+ int UnusedField;
+
+ [Kept]
+ int UnusedField2;
+ }
+
+ [Kept]
+ [KeptAttributeAttribute (typeof (IsByRefLikeAttribute))]
+ [KeptAttributeAttribute (typeof (CompilerFeatureRequiredAttribute))]
+ [KeptAttributeAttribute (typeof (ObsoleteAttribute))]
+ ref struct ReferencedRefStruct
+ {
+ [Kept]
+ public ref int UnusedRefField;
+
+ [Kept]
+ public ref ReferencedType UnusedClass;
+
+ [Kept]
+ int UnusedField;
+ }
+
+ [Kept]
+ class ReferencedType
+ {
+ int field;
+ }
}
} \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/RefFieldDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/RefFieldDataFlow.cs
new file mode 100644
index 000000000..5fb0aa993
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/RefFieldDataFlow.cs
@@ -0,0 +1,560 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Helpers;
+using DAM = System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute;
+using DAMT = System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes;
+
+namespace Mono.Linker.Tests.Cases.DataFlow
+{
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ class RefFieldDataFlow
+ {
+ [Kept]
+ // Bug for the IL2069's here: https://github.com/dotnet/linker/issues/2874
+ [ExpectedWarning ("IL2069", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2069", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2069", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2069", ProducedBy = ProducedBy.Trimmer)]
+ public static void Main ()
+ {
+ RefFieldWithMethods withMethods = new (ref fieldWithMethods);
+ RefFieldWithFields withFields = new (ref fieldWithFields);
+ RefFieldWithMethodsAndFields withMethodsAndFields = new (ref fieldWithMethodsAndFields);
+ RefFieldUnannotated unannotated = new (ref field);
+
+ AssignLocals<int, int, int, int> (withMethods);
+ AssignRefToLocals<int, int, int, int> (withMethods);
+ AssignRefLocals<int, int, int, int> (withMethods, withFields, withMethodsAndFields, unannotated);
+ AssignRefsToFields (withMethods);
+ AssignParameters (withMethods, typeof (int), typeof (int), typeof (int), typeof (int));
+ AssignRefParameters<int, int, int, int> (withMethods, ref field, ref fieldWithMethods, ref fieldWithFields, ref fieldWithMethodsAndFields);
+ AssignFields (withMethods, null, null, null, null);
+ AssignRefFields (withMethods, unannotated, withMethods, withFields, withMethodsAndFields);
+ AssignReturns<int, int, int, int> (withMethods);
+ AssignRefReturns<int, int, int, int> (withMethods);
+ AssignProperties (withMethods);
+ AssignRefProperties (withMethods);
+ }
+ static Type field = typeof (int);
+
+ [DAM (DAMT.PublicMethods)]
+ static Type fieldWithMethods = typeof (int);
+
+ [DAM (DAMT.PublicFields)]
+ static Type fieldWithFields = typeof (int);
+
+ [DAM (DAMT.PublicMethods | DAMT.PublicFields)]
+ static Type fieldWithMethodsAndFields = typeof (int);
+
+ [ExpectedWarning ("IL2089", "T", "T")]
+ [ExpectedWarning ("IL2089", "T", "TF")]
+ static void AssignLocals<
+ T,
+ [DAM (DAMT.PublicMethods)] TM,
+ [DAM (DAMT.PublicFields)] TF,
+ [DAM (DAMT.PublicFields | DAMT.PublicMethods)] TMF>
+ (RefFieldWithMethods target)
+ {
+ var t = typeof (T);
+ target.T = t; // Warn
+
+ t = typeof (TM);
+ target.T = t; // Okay
+
+ t = typeof (TF);
+ target.T = t; // Warn
+
+ t = typeof (TMF);
+ target.T = t; // Okay
+ }
+
+ [ExpectedWarning ("IL2089", "T")]
+ [ExpectedWarning ("IL2089", "TF")]
+ static void AssignRefToLocals<
+ T,
+ [DAM (DAMT.PublicMethods)] TM,
+ [DAM (DAMT.PublicFields)] TF,
+ [DAM (DAMT.PublicFields | DAMT.PublicMethods)] TMF>
+ (scoped RefFieldWithMethods target)
+ {
+ var t = typeof (T);
+ target.T = ref t; // Warn
+
+ var tf = typeof (TF);
+ target.T = ref tf; // Warn
+
+ var tm = typeof (TM);
+ target.T = ref tm;
+ tm = typeof (TF); // This is a hole that doesn't warn
+
+ var tmf = typeof (TMF);
+ target.T = ref tmf;
+ target.T = typeof (TM);
+ tmf = typeof (TF); // This is a hole that doesn't warn but assigns a misannotated value to target.T
+ }
+
+ [ExpectedWarning ("IL2089", "RefFieldWithMethods", "T", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2089", "RefFieldWithFields", "T", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2089", "RefFieldWithMethodsAndFields", "T", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2089", "RefFieldWithMethodsAndFields", "T", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2089", "RefFieldWithFields", "T", ProducedBy = ProducedBy.Trimmer)]
+ static void AssignRefLocals<
+ T,
+ [DAM (DAMT.PublicMethods)] TM,
+ [DAM (DAMT.PublicFields)] TF,
+ [DAM (DAMT.PublicFields | DAMT.PublicFields)] TMF>
+ (scoped RefFieldWithMethods withMethods,
+ scoped RefFieldWithFields withFields,
+ scoped RefFieldWithMethodsAndFields withMethodsAndFields,
+ scoped RefFieldUnannotated unannotated)
+ {
+ ref Type t = ref unannotated.T;
+ // The following create holes where the local can assign a misannotated value to the annotated ref field
+ ref Type tm = ref withMethods.T;
+ ref Type tf = ref withFields.T;
+ ref Type tmf = ref withMethodsAndFields.T;
+
+ // Okay
+ t = typeof (T);
+ tf = typeof (TF);
+ tm = typeof (TM);
+ tmf = typeof (TMF);
+
+ t = typeof (T);
+ tm = typeof (T); // Hole: assigns unannotated T to withMethods.T
+ tf = typeof (T); // Hole: assigns unannotated T to withFields.T
+ tmf = typeof (T); // Hole: assigns unannotated T to withMethodsAndFields.T
+
+ t = ref tf;
+ t = typeof (T); // Hole: assigns unannotated T to withFields.T
+ }
+
+ [ExpectedWarning ("IL2079", "UnannotatedField.T")]
+ static void AssignRefsToFields (RefFieldWithMethods target)
+ {
+ var x = new UnannotatedField { T = typeof (int) };
+ target.T = ref x.T; // Warn
+
+ var withMethods = new FieldWithMethods { T = typeof (int) };
+ target.T = ref withMethods.T; // Okay
+
+ var withMethodsAndFields = new FieldWithMethodsAndFields { T = typeof (int) };
+ target.T = ref withMethodsAndFields.T; // Creates hole
+ target.T = withMethods.T; // Hole: assigns a value with Methods to a value annotated with Methods and Fields
+ }
+
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "param")]
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "param")]
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "paramWithFields")]
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "paramWithFields")]
+ static void AssignParameters (scoped RefFieldWithMethods target,
+ Type param,
+ [DAM (DAMT.PublicMethods)] Type paramWithMethods,
+ [DAM (DAMT.PublicFields)] Type paramWithFields,
+ [DAM (DAMT.PublicMethods | DAMT.PublicFields)] Type paramWithMethodsAndFields)
+ {
+ target.T = param; // Warn
+ target.T = ref param; // Warn
+
+ target.T = paramWithMethods;
+ target.T = ref paramWithMethods;
+
+ target.T = paramWithFields; // Warn
+ target.T = ref paramWithFields; // Warn
+
+ target.T = paramWithMethodsAndFields; // Okay
+ target.T = ref paramWithMethodsAndFields; // Creates hole
+ target.T = paramWithMethods; // Hole: assigns value with Methods to a value annotated with Methods and Fields
+ }
+
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "param")]
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "paramWithFields")]
+ [ExpectedWarning ("IL2077", "paramWithMethodsAndFields", "RefFieldWithMethods.T")]
+ // Linker doesn't recognize ldind.ref
+ // https://github.com/dotnet/linker/issues/2943
+ // IL2064's are bugs - shouldn't be unknown values
+ [ExpectedWarning ("IL2064", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2064", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2064", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2064", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2064", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "param", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2069", "RefFieldWithMethods.T", "paramWithFields", ProducedBy = ProducedBy.Analyzer)]
+ static void AssignRefParameters<
+ T,
+ [DAM (DAMT.PublicMethods)] TM,
+ [DAM (DAMT.PublicFields)] TF,
+ [DAM (DAMT.PublicFields | DAMT.PublicMethods)] TMF>
+ (scoped RefFieldWithMethods target,
+ scoped ref Type param,
+ [DAM (DAMT.PublicMethods)] scoped ref Type paramWithMethods,
+ [DAM (DAMT.PublicFields)] scoped ref Type paramWithFields,
+ [DAM (DAMT.PublicMethods | DAMT.PublicFields)] scoped ref Type paramWithMethodsAndFields)
+ {
+ target.T = param; // Warn
+ target.T = ref param; // Warn
+
+ target.T = paramWithMethods;
+ target.T = ref paramWithMethods;
+
+ target.T = paramWithFields; // Warn
+ target.T = ref paramWithFields; // Warn
+
+ target.T = paramWithMethodsAndFields; // Okay
+ target.T = ref paramWithMethodsAndFields; // Creates hole
+ target.T = paramWithMethods; // Hole: assigns value with Methods to a value annotated with Methods and Fields
+
+ param = ref target.T; // Creates hole
+ param = typeof (T); // Hole: assigns unannotated value to location pointed to by annotated ref field
+
+ paramWithMethodsAndFields = ref target.T; // Warn
+ }
+
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "UnannotatedField.T")]
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "UnannotatedField.T")]
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "FieldWithFields.T")]
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "FieldWithFields.T")]
+ static void AssignFields (RefFieldWithMethods target, UnannotatedField unannotated, FieldWithMethods withMethods, FieldWithFields withFields, FieldWithMethodsAndFields withMethodsAndFields)
+ {
+ target.T = unannotated.T; // Warn
+ target.T = withMethods.T;
+ target.T = withFields.T; // Warn
+ target.T = withMethodsAndFields.T;
+
+ target.T = ref unannotated.T; // Warn
+ target.T = ref withMethods.T;
+ target.T = ref withFields.T; // Warn
+ target.T = ref withMethodsAndFields.T; // Creates hole
+ target.T = withMethods.T; // Hole: assigns value with methods to location pointed to by ref field with methods and fields
+ }
+
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "RefFieldUnannotated.T")]
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "RefFieldWithFields.T")]
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "RefFieldUnannotated.T", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2079", "RefFieldWithMethods.T", "RefFieldWithFields.T", ProducedBy = ProducedBy.Analyzer)]
+ // IL2064's are bugs - shouldn't be unknown values
+ // https://github.com/dotnet/linker/issues/2943
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = unannotated.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withMethods.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withFields.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withMethodsAndFields.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withMethodsAndFields.T;
+ static void AssignRefFields (
+ RefFieldWithMethods target,
+ RefFieldUnannotated unannotated,
+ RefFieldWithMethods withMethods,
+ RefFieldWithFields withFields,
+ RefFieldWithMethodsAndFields withMethodsAndFields)
+ {
+ target.T = unannotated.T; // Warn
+ target.T = withMethods.T;
+ target.T = withFields.T; // Warn
+ target.T = withMethodsAndFields.T;
+
+ target.T = ref unannotated.T; // Warn
+ target.T = ref withMethods.T;
+ target.T = ref withFields.T; // Warn
+ target.T = ref withMethodsAndFields.T; // Creates hole
+ target.T = withMethods.T; // Hole: assigns value with methods to location pointed to by ref field with methods and fields
+ }
+
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetRefUnannotated")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetRefWithFields")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetRefUnannotated")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetRefWithFields")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetRefUnannotated", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetRefWithFields", ProducedBy = ProducedBy.Analyzer)]
+ // IL2064's are bugs - shouldn't be unknown values
+ // https://github.com/dotnet/linker/issues/2943
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ static void AssignRefReturns<
+ T,
+ [DAM (DAMT.PublicMethods)] TM,
+ [DAM (DAMT.PublicFields)] TF,
+ [DAM (DAMT.PublicFields | DAMT.PublicMethods)] TMF>
+ (RefFieldWithMethods target)
+ {
+ target.T = ref GetRefUnannotated (); // Warn
+ target.T = ref GetRefWithMethods ();
+ target.T = ref GetRefWithFields (); // Warn
+ target.T = ref GetRefWithMethodsAndFields (); // Creates hole
+ target.T = typeof (TM);
+
+ ref Type t = ref GetRefUnannotated ();
+ target.T = t; // Warn
+ target.T = ref t; // Warn
+
+ t = ref GetRefWithMethods ();
+ target.T = t;
+ target.T = ref t;
+
+ t = ref GetRefWithFields ();
+ target.T = t; // Warn
+ target.T = ref t; // Warn
+
+ t = ref GetRefWithMethodsAndFields ();
+ target.T = t; // Bug: Warns with IL2064
+ target.T = ref t; // Creates hole
+ target.T = typeof (TM);
+ }
+
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetUnannotated")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetWithFields")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetUnannotated")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "GetWithFields")]
+ static void AssignReturns<
+ T,
+ [DAM (DAMT.PublicMethods)] TM,
+ [DAM (DAMT.PublicFields)] TF,
+ [DAM (DAMT.PublicFields | DAMT.PublicMethods)] TMF>
+ (scoped RefFieldWithMethods target)
+ {
+ Type t = GetUnannotated ();
+ target.T = t; // Warn
+ target.T = ref t; // Warn
+
+ t = GetWithMethods ();
+ target.T = t;
+ target.T = ref t;
+
+ t = GetWithFields ();
+ target.T = t; // Warn
+ target.T = ref t; // Warn
+
+ t = GetWithMethodsAndFields ();
+ target.T = t; // Okay
+ target.T = ref t; // Creates hole
+ target.T = typeof (TM);
+ }
+
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "PropUnannotated.T")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "PropWithFields.T")]
+ static void AssignProperties (RefFieldWithMethods target,
+ PropUnannotated unannotated = null,
+ PropWithMethods withMethods = null,
+ PropWithFields withFields = null,
+ PropWithMethodsAndFields withMethodsAndFields = null)
+ {
+ target.T = unannotated.T; // Warn
+ target.T = withMethods.T; // Okay
+ target.T = withFields.T; // Warn
+ target.T = withMethodsAndFields.T;// Okay
+ }
+
+ // target.T = x.T
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "RefPropUnannotated.T", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "RefPropWithFields.T", ProducedBy = ProducedBy.Analyzer)]
+ // target.T = t;
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "RefPropUnannotated.T", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "RefPropWithFields.T", ProducedBy = ProducedBy.Analyzer)]
+ // target.T = ref x.T
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "RefPropUnannotated.T")]
+ [ExpectedWarning ("IL2074", "RefFieldWithMethods.T", "RefPropWithFields.T")]
+ // ref Type t = ref x.T; target.T = t;
+ // IL2064's are bugs - shouldn't be unknown values
+ // https://github.com/dotnet/linker/issues/2943
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = unannotated.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withMethods.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withFields.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withMethodsAndFieldswithMtho.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = withMethods.T;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ [ExpectedWarning ("IL2064", "RefFieldWithMethods.T", ProducedBy = ProducedBy.Trimmer)] // target.T = t;
+ static void AssignRefProperties (RefFieldWithMethods target,
+ RefPropUnannotated unannotated = null,
+ RefPropWithMethods withMethods = null,
+ RefPropWithFields withFields = null,
+ RefPropWithMethodsAndFields withMethodsAndFields = null)
+ {
+ // All cause IL2064 -- linker doesn't recognize ldind.ref
+ target.T = unannotated.T; // Warn
+ target.T = withMethods.T; // Okay
+ target.T = withFields.T; // Warn
+ target.T = withMethodsAndFields.T;// Okay
+
+ target.T = ref unannotated.T; // Warn
+ target.T = ref withMethods.T; // Okay
+ target.T = ref withFields.T; // Warn
+ target.T = ref withMethodsAndFields.T; // Creates hole
+ target.T = withMethods.T; // Assigns a value with methods to a location with methods and fields
+
+
+ ref Type t = ref unannotated.T;
+ target.T = t; // Okay
+ t = ref withMethods.T;
+ target.T = t; // Creates hole
+ t = ref withFields.T;
+ target.T = t; // Creates hole
+ t = ref withMethodsAndFields.T;
+ target.T = t; // Creates hole
+ }
+
+ class PropUnannotated
+ {
+ public Type T { get; set; }
+ }
+ class PropWithMethods
+ {
+ [DAM (DAMT.PublicMethods)]
+ public Type T { get; set; }
+ }
+ class PropWithFields
+ {
+ [DAM (DAMT.PublicFields)]
+ public Type T { get; set; }
+ }
+ class PropWithMethodsAndFields
+ {
+ [DAM (DAMT.PublicMethods | DAMT.PublicFields)]
+ public Type T { get; set; }
+ }
+ class RefPropUnannotated
+ {
+ Type _field = null;
+
+ public ref Type T { get => ref _field; }
+ }
+ class RefPropWithMethods
+ {
+ [DAM (DAMT.PublicMethods)]
+ Type _field = null;
+
+ [DAM (DAMT.PublicMethods)]
+ public ref Type T { get => ref _field; }
+ }
+ class RefPropWithFields
+ {
+ [DAM (DAMT.PublicFields)]
+ Type _field = null;
+
+ [DAM (DAMT.PublicFields)]
+ public ref Type T { get => ref _field; }
+ }
+ class RefPropWithMethodsAndFields
+ {
+ [DAM (DAMT.PublicFields | DAMT.PublicMethods)]
+ Type _field = null;
+
+ [DAM (DAMT.PublicFields | DAMT.PublicMethods)]
+ public ref Type T { get => ref _field; }
+ }
+ class UnannotatedField
+ {
+ public Type T;
+ }
+
+ class FieldWithMethods
+ {
+ [DAM (DAMT.PublicMethods)]
+ public Type T;
+ }
+
+ class FieldWithFields
+ {
+ [DAM (DAMT.PublicFields)]
+ public Type T;
+ }
+
+ class FieldWithMethodsAndFields
+ {
+ [DAM (DAMT.PublicMethods | DAMT.PublicFields)]
+ public Type T;
+ }
+
+ static Type GetUnannotated ()
+ => throw new NotImplementedException ();
+
+ static ref Type GetRefUnannotated ()
+ => throw new NotImplementedException ();
+
+ [return: DAM (DAMT.PublicMethods)]
+ static Type GetWithMethods ()
+ => throw new NotImplementedException ();
+
+ [return: DAM (DAMT.PublicMethods)]
+ static ref Type GetRefWithMethods ()
+ => throw new NotImplementedException ();
+
+ [return: DAM (DAMT.PublicFields)]
+ static Type GetWithFields ()
+ => throw new NotImplementedException ();
+
+ [return: DAM (DAMT.PublicFields)]
+ static ref Type GetRefWithFields ()
+ => throw new NotImplementedException ();
+
+ [return: DAM (DAMT.PublicFields | DAMT.PublicMethods)]
+ public static Type GetWithMethodsAndFields ()
+ => throw new NotImplementedException ();
+
+ [return: DAM (DAMT.PublicFields | DAMT.PublicMethods)]
+ public static ref Type GetRefWithMethodsAndFields ()
+ => throw new NotImplementedException ();
+
+ [Kept]
+ ref struct RefFieldUnannotated
+ {
+ [Kept]
+ public ref Type T;
+
+ [Kept]
+ public RefFieldUnannotated (ref Type t)
+ {
+ T = ref t;
+ }
+ }
+
+ [Kept]
+ ref struct RefFieldWithMethods
+ {
+ [Kept]
+ [DynamicallyAccessedMembers (DAMT.PublicMethods)]
+ public ref Type T;
+
+ [Kept]
+ public RefFieldWithMethods ([DynamicallyAccessedMembers (DAMT.PublicMethods)] ref Type t)
+ {
+ T = ref t;
+ }
+ }
+
+ [Kept]
+ ref struct RefFieldWithFields
+ {
+ [Kept]
+ [DynamicallyAccessedMembers (DAMT.PublicFields)]
+ public ref Type T;
+
+ [Kept]
+ public RefFieldWithFields ([DynamicallyAccessedMembers (DAMT.PublicFields)] ref Type t)
+ {
+ T = ref t;
+ }
+ }
+
+ [Kept]
+ ref struct RefFieldWithMethodsAndFields
+ {
+ [Kept]
+ [DynamicallyAccessedMembers (DAMT.PublicFields | DAMT.PublicMethods)]
+ public ref Type T;
+
+ [Kept]
+ public RefFieldWithMethodsAndFields ([DynamicallyAccessedMembers (DAMT.PublicFields | DAMT.PublicMethods)] ref Type t)
+ {
+ T = ref t;
+ }
+ }
+ }
+}