diff options
author | Jackson Schuster <36744439+jtschuster@users.noreply.github.com> | 2022-08-12 21:22:45 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-12 21:22:45 +0300 |
commit | 1ef0acf496369c15911dd5fe78e2d100659fa389 (patch) | |
tree | 0cfd25aef8d94fa95cf6530fe255bca91f2c012d | |
parent | d559347987ba45c0dc3389cb96dce1ae20204513 (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.cs | 33 | ||||
-rw-r--r-- | src/linker/Linker.Steps/MarkStep.cs | 43 | ||||
-rw-r--r-- | test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs | 6 | ||||
-rw-r--r-- | test/Mono.Linker.Tests.Cases/Basic/UnusedFieldsOfStructsAreKept.cs | 41 | ||||
-rw-r--r-- | test/Mono.Linker.Tests.Cases/DataFlow/RefFieldDataFlow.cs | 560 |
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; + } + } + } +} |