diff --git a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator_RefSafety.cs b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator_RefSafety.cs new file mode 100644 index 0000000000000..07ea26d9bc8f0 --- /dev/null +++ b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator_RefSafety.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CSharp.Symbols; + +namespace Microsoft.CodeAnalysis.CSharp.CodeGen; + +internal partial class CodeGenerator +{ + /// + private static bool MightEscapeTemporaryRefs(BoundCall node, bool used) + { + return MightEscapeTemporaryRefs( + used: used, + returnType: node.Type, + returnRefKind: node.Method.RefKind, + thisParameterSymbol: node.Method.TryGetThisParameter(out var thisParameter) ? thisParameter : null, + parameters: node.Method.Parameters); + } + + /// + private static bool MightEscapeTemporaryRefs(BoundObjectCreationExpression node, bool used) + { + return MightEscapeTemporaryRefs( + used: used, + returnType: node.Type, + returnRefKind: RefKind.None, + thisParameterSymbol: null, + parameters: node.Constructor.Parameters); + } + + /// + private static bool MightEscapeTemporaryRefs(BoundFunctionPointerInvocation node, bool used) + { + FunctionPointerMethodSymbol method = node.FunctionPointer.Signature; + return MightEscapeTemporaryRefs( + used: used, + returnType: node.Type, + returnRefKind: method.RefKind, + thisParameterSymbol: null, + parameters: method.Parameters); + } + + /// + /// Determines whether a 'ref' can be captured by the call (considering only its signature). + /// + /// + /// + /// The emit layer consults this to avoid reusing temporaries that are passed by ref to such methods. + /// + /// + /// This is a heuristic which might have false positives, i.e., + /// it might not recognize that a call cannot capture a 'ref' + /// even if the binding-time ref safety analysis would recognize that. + /// That is fine, the IL will be correct, albeit less optimal (it might use more temps). + /// But we still want some heuristic to avoid regressing IL of common safe cases. + /// + /// + private static bool MightEscapeTemporaryRefs( + bool used, + TypeSymbol returnType, + RefKind returnRefKind, + ParameterSymbol? thisParameterSymbol, + ImmutableArray parameters) + { + // whether we have any outputs that can capture `ref`s + bool anyRefTargets = false; + // whether we have any inputs that can contain `ref`s + bool anyRefSources = false; + + if (used && (returnRefKind != RefKind.None || returnType.IsRefLikeOrAllowsRefLikeType())) + { + // If returning by ref or returning a ref struct, the result might capture `ref`s. + anyRefTargets = true; + } + + if (thisParameterSymbol is not null && processParameter(thisParameterSymbol, anyRefSources: ref anyRefSources, anyRefTargets: ref anyRefTargets)) + { + return true; + } + + foreach (var parameter in parameters) + { + if (processParameter(parameter, anyRefSources: ref anyRefSources, anyRefTargets: ref anyRefTargets)) + { + return true; + } + } + + return false; + + // Returns true if we can return 'true' early. + static bool processParameter(ParameterSymbol parameter, ref bool anyRefSources, ref bool anyRefTargets) + { + if (parameter.Type.IsRefLikeOrAllowsRefLikeType() && parameter.EffectiveScope != ScopedKind.ScopedValue) + { + anyRefSources = true; + if (parameter.RefKind.IsWritableReference()) + { + anyRefTargets = true; + } + } + else if (parameter.RefKind != RefKind.None && parameter.EffectiveScope == ScopedKind.None) + { + anyRefSources = true; + } + + // If there is at least one output and at least one input, a `ref` can be captured. + return anyRefTargets && anyRefSources; + } + } +} diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 86b3e463535e1..b984244ad4d74 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -1660,7 +1660,13 @@ private void EmitStaticCallExpression(BoundCall call, UseKind useKind) Debug.Assert(method.IsStatic); + var countBefore = _builder.LocalSlotManager.StartScopeOfTrackingAddressedLocals(); + EmitArguments(arguments, method.Parameters, call.ArgumentRefKindsOpt); + + _builder.LocalSlotManager.EndScopeOfTrackingAddressedLocals(countBefore, + MightEscapeTemporaryRefs(call, used: useKind != UseKind.Unused)); + int stackBehavior = GetCallStackBehavior(method, arguments); if (method.IsAbstract || method.IsVirtual) @@ -1690,6 +1696,8 @@ private void EmitInstanceCallExpression(BoundCall call, UseKind useKind) bool box; LocalDefinition tempOpt; + var countBefore = _builder.LocalSlotManager.StartScopeOfTrackingAddressedLocals(); + if (receiverIsInstanceCall(call, out BoundCall nested)) { var calls = ArrayBuilder.GetInstance(); @@ -1755,6 +1763,11 @@ private void EmitInstanceCallExpression(BoundCall call, UseKind useKind) } emitArgumentsAndCallEpilogue(call, callKind, receiverUseKind); + + _builder.LocalSlotManager.EndScopeOfTrackingAddressedLocals(countBefore, MightEscapeTemporaryRefs(call, used: true)); + + countBefore = _builder.LocalSlotManager.StartScopeOfTrackingAddressedLocals(); + FreeOptTemp(tempOpt); tempOpt = null; @@ -1815,6 +1828,9 @@ private void EmitInstanceCallExpression(BoundCall call, UseKind useKind) } emitArgumentsAndCallEpilogue(call, callKind, useKind); + + _builder.LocalSlotManager.EndScopeOfTrackingAddressedLocals(countBefore, MightEscapeTemporaryRefs(call, used: useKind != UseKind.Unused)); + FreeOptTemp(tempOpt); return; @@ -2446,8 +2462,14 @@ private void EmitObjectCreationExpression(BoundObjectCreationExpression expressi } // none of the above cases, so just create an instance + + var countBefore = _builder.LocalSlotManager.StartScopeOfTrackingAddressedLocals(); + EmitArguments(expression.Arguments, constructor.Parameters, expression.ArgumentRefKindsOpt); + _builder.LocalSlotManager.EndScopeOfTrackingAddressedLocals(countBefore, + MightEscapeTemporaryRefs(expression, used)); + var stackAdjustment = GetObjCreationStackBehavior(expression); _builder.EmitOpCode(ILOpCode.Newobj, stackAdjustment); @@ -2704,7 +2726,14 @@ private bool TryInPlaceCtorCall(BoundExpression target, BoundObjectCreationExpre Debug.Assert(temp == null, "in-place ctor target should not create temps"); var constructor = objCreation.Constructor; + + var countBefore = _builder.LocalSlotManager.StartScopeOfTrackingAddressedLocals(); + EmitArguments(objCreation.Arguments, constructor.Parameters, objCreation.ArgumentRefKindsOpt); + + _builder.LocalSlotManager.EndScopeOfTrackingAddressedLocals(countBefore, + MightEscapeTemporaryRefs(objCreation, used)); + // -2 to adjust for consumed target address and not produced value. var stackAdjustment = GetObjCreationStackBehavior(objCreation) - 2; _builder.EmitOpCode(ILOpCode.Call, stackAdjustment); @@ -4026,7 +4055,14 @@ private void EmitCalli(BoundFunctionPointerInvocation ptrInvocation, UseKind use } FunctionPointerMethodSymbol method = ptrInvocation.FunctionPointer.Signature; + + var countBefore = _builder.LocalSlotManager.StartScopeOfTrackingAddressedLocals(); + EmitArguments(ptrInvocation.Arguments, method.Parameters, ptrInvocation.ArgumentRefKindsOpt); + + _builder.LocalSlotManager.EndScopeOfTrackingAddressedLocals(countBefore, + MightEscapeTemporaryRefs(ptrInvocation, used: useKind != UseKind.Unused)); + var stackBehavior = GetCallStackBehavior(ptrInvocation.FunctionPointer.Signature, ptrInvocation.Arguments); if (temp is object) diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index b3359712b498d..87cd14d63032f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -368,6 +368,20 @@ public override ImmutableArray Parameters } } + internal override bool TryGetThisParameter(out ParameterSymbol? thisParameter) + { + if (UnderlyingMethod.TryGetThisParameter(out ParameterSymbol? underlyingThisParameter)) + { + thisParameter = underlyingThisParameter != null + ? new ThisParameterSymbol(this, _container) + : null; + return true; + } + + thisParameter = null; + return false; + } + public override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; public override ImmutableArray RefCustomModifiers => UnderlyingMethod.RefCustomModifiers; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs index 1f87e26e45231..b34b097c130ad 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs @@ -861,7 +861,8 @@ static ref readonly int M(in int x) // Code size 72 (0x48) .maxstack 3 .locals init (int V_0, - int V_1) + int V_1, + int V_2) IL_0000: ldc.i4.s 42 IL_0002: stloc.0 IL_0003: ldloca.s V_0 @@ -870,22 +871,22 @@ .locals init (int V_0, IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: newobj ""Program..ctor()"" IL_0015: ldc.i4.5 - IL_0016: stloc.0 - IL_0017: ldloca.s V_0 + IL_0016: stloc.1 + IL_0017: ldloca.s V_1 IL_0019: ldc.i4.6 - IL_001a: stloc.1 - IL_001b: ldloca.s V_1 + IL_001a: stloc.2 + IL_001b: ldloca.s V_2 IL_001d: call ""int Program.this[in int, in int].get"" IL_0022: call ""void System.Console.WriteLine(int)"" IL_0027: ldc.i4.s 42 - IL_0029: stloc.0 - IL_002a: ldloca.s V_0 + IL_0029: stloc.1 + IL_002a: ldloca.s V_1 IL_002c: call ""ref readonly int Program.M(in int)"" IL_0031: ldind.i4 IL_0032: call ""void System.Console.WriteLine(int)"" IL_0037: ldc.i4.s 42 - IL_0039: stloc.0 - IL_003a: ldloca.s V_0 + IL_0039: stloc.2 + IL_003a: ldloca.s V_2 IL_003c: call ""ref readonly int Program.M(in int)"" IL_0041: ldind.i4 IL_0042: call ""void System.Console.WriteLine(int)"" diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index 292b8336a9ee2..41b3ab5677a5b 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -9525,7 +9525,8 @@ .locals init (System.ReadOnlySpan V_0, //y System.ReadOnlySpan V_3, int V_4, int[] V_5, - System.Span V_6) + System.Span V_6, + System.Span V_7) IL_0000: ldtoken ".__StaticArrayInitTypeSize=8_Align=4 .34FB5C825DE7CA4AEA6E712F19D439C1DA0C92C37B423936C5F618545CA4FA1F4" IL_0005: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" IL_000a: stloc.0 @@ -9561,8 +9562,8 @@ .locals init (System.ReadOnlySpan V_0, //y IL_0056: ldloca.s V_3 IL_0058: ldloc.s V_5 IL_005a: newobj "System.Span..ctor(int[])" - IL_005f: stloc.s V_6 - IL_0061: ldloca.s V_6 + IL_005f: stloc.s V_7 + IL_0061: ldloca.s V_7 IL_0063: ldloc.s V_4 IL_0065: ldloca.s V_3 IL_0067: call "int System.ReadOnlySpan.Length.get" @@ -11541,7 +11542,8 @@ .locals init (System.Collections.Generic.List V_0, T[] V_2, System.Span V_3, System.Span V_4, - System.Span V_5) + System.Span V_5, + System.Span V_6) IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stloc.0 @@ -11577,8 +11579,8 @@ .locals init (System.Collections.Generic.List V_0, IL_004e: ldloca.s V_4 IL_0050: ldloc.2 IL_0051: newobj "System.Span..ctor(T[])" - IL_0056: stloc.s V_5 - IL_0058: ldloca.s V_5 + IL_0056: stloc.s V_6 + IL_0058: ldloca.s V_6 IL_005a: ldloc.1 IL_005b: ldloca.s V_4 IL_005d: call "int System.Span.Length.get" @@ -11645,7 +11647,8 @@ .locals init (int[] V_0, int V_10, int V_11, int V_12, - int V_13) + int V_13, + System.Span V_14) IL_0000: ldstr "A" IL_0005: ldc.i4.2 IL_0006: newarr "int" @@ -11762,8 +11765,8 @@ .locals init (int[] V_0, IL_0105: ldloca.s V_6 IL_0107: ldloc.s V_4 IL_0109: newobj "System.Span..ctor(int[])" - IL_010e: stloc.s V_7 - IL_0110: ldloca.s V_7 + IL_010e: stloc.s V_14 + IL_0110: ldloca.s V_14 IL_0112: ldloc.3 IL_0113: ldloca.s V_6 IL_0115: call "int System.Span.Length.get" @@ -33712,7 +33715,8 @@ .locals init (System.ReadOnlySpan V_0, //li1 System.ReadOnlySpan V_3, int V_4, int[] V_5, - System.Span V_6) + System.Span V_6, + System.Span V_7) IL_0000: ldtoken ".__StaticArrayInitTypeSize=12_Align=4 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" IL_000a: stloc.0 @@ -33754,8 +33758,8 @@ .locals init (System.ReadOnlySpan V_0, //li1 IL_006d: ldloca.s V_3 IL_006f: ldloc.s V_5 IL_0071: newobj "System.Span..ctor(int[])" - IL_0076: stloc.s V_6 - IL_0078: ldloca.s V_6 + IL_0076: stloc.s V_7 + IL_0078: ldloca.s V_7 IL_007a: ldloc.s V_4 IL_007c: ldloca.s V_3 IL_007e: call "int System.ReadOnlySpan.Length.get" @@ -33811,7 +33815,8 @@ .locals init (System.ReadOnlySpan V_0, //li1 C[] V_6, System.ReadOnlySpan.Enumerator V_7, D V_8, - System.ReadOnlySpan V_9) + System.ReadOnlySpan.Enumerator V_9, + System.ReadOnlySpan V_10) IL_0000: newobj "D..ctor()" IL_0005: stloc.1 IL_0006: ldloca.s V_1 @@ -33854,9 +33859,9 @@ .locals init (System.ReadOnlySpan V_0, //li1 IL_0061: brtrue.s IL_0043 IL_0063: ldloca.s V_4 IL_0065: call "System.ReadOnlySpan.Enumerator System.ReadOnlySpan.GetEnumerator()" - IL_006a: stloc.s V_7 + IL_006a: stloc.s V_9 IL_006c: br.s IL_0085 - IL_006e: ldloca.s V_7 + IL_006e: ldloca.s V_9 IL_0070: call "ref readonly D System.ReadOnlySpan.Enumerator.Current.get" IL_0075: ldind.ref IL_0076: stloc.s V_8 @@ -33868,13 +33873,13 @@ .locals init (System.ReadOnlySpan V_0, //li1 IL_0081: ldc.i4.1 IL_0082: add IL_0083: stloc.s V_5 - IL_0085: ldloca.s V_7 + IL_0085: ldloca.s V_9 IL_0087: call "bool System.ReadOnlySpan.Enumerator.MoveNext()" IL_008c: brtrue.s IL_006e IL_008e: ldloc.s V_6 IL_0090: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(C[])" - IL_0095: stloc.s V_9 - IL_0097: ldloca.s V_9 + IL_0095: stloc.s V_10 + IL_0097: ldloca.s V_10 IL_0099: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" IL_009e: ret } @@ -34231,7 +34236,8 @@ .locals init (Base[] V_0, int V_3, Base[] V_4, System.ReadOnlySpan V_5, - System.Span V_6) + System.Span V_6, + System.Span V_7) IL_0000: ldc.i4.1 IL_0001: newarr "Derived" IL_0006: dup @@ -34283,8 +34289,8 @@ .locals init (Base[] V_0, IL_006a: ldloca.s V_5 IL_006c: ldloc.s V_4 IL_006e: newobj "System.Span..ctor(Base[])" - IL_0073: stloc.s V_6 - IL_0075: ldloca.s V_6 + IL_0073: stloc.s V_7 + IL_0075: ldloca.s V_7 IL_0077: ldloc.3 IL_0078: ldloca.s V_5 IL_007a: call "int System.ReadOnlySpan.Length.get" @@ -35283,7 +35289,8 @@ .locals init (int[] V_0, int[] V_3, System.ReadOnlySpan V_4, System.ReadOnlySpan V_5, - System.Span V_6) + System.Span V_6, + System.Span V_7) IL_0000: ldc.i4.2 IL_0001: newarr "int" IL_0006: dup @@ -35335,8 +35342,8 @@ .locals init (int[] V_0, IL_005f: ldloca.s V_5 IL_0061: ldloc.3 IL_0062: newobj "System.Span..ctor(int[])" - IL_0067: stloc.s V_6 - IL_0069: ldloca.s V_6 + IL_0067: stloc.s V_7 + IL_0069: ldloca.s V_7 IL_006b: ldloc.2 IL_006c: ldloca.s V_5 IL_006e: call "int System.ReadOnlySpan.Length.get" @@ -35564,7 +35571,8 @@ .locals init (int V_0, System.ReadOnlySpan V_6, System.Runtime.CompilerServices.TaskAwaiter V_7, System.Span V_8, - System.Exception V_9) + System.Span V_9, + System.Exception V_10) IL_0000: ldarg.0 IL_0001: ldfld "int C.
d__0.<>1__state" IL_0006: stloc.0 @@ -35659,8 +35667,8 @@ .locals init (int V_0, IL_00ea: ldarg.0 IL_00eb: ldfld "int[] C.
d__0.<>7__wrap4" IL_00f0: newobj "System.Span..ctor(int[])" - IL_00f5: stloc.s V_8 - IL_00f7: ldloca.s V_8 + IL_00f5: stloc.s V_9 + IL_00f7: ldloca.s V_9 IL_00f9: ldloc.s V_4 IL_00fb: ldloca.s V_6 IL_00fd: call "int System.ReadOnlySpan.Length.get" @@ -35739,13 +35747,13 @@ .locals init (int V_0, } catch System.Exception { - IL_01c5: stloc.s V_9 + IL_01c5: stloc.s V_10 IL_01c7: ldarg.0 IL_01c8: ldc.i4.s -2 IL_01ca: stfld "int C.
d__0.<>1__state" IL_01cf: ldarg.0 IL_01d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" - IL_01d5: ldloc.s V_9 + IL_01d5: ldloc.s V_10 IL_01d7: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" IL_01dc: leave.s IL_01f1 } @@ -35872,13 +35880,14 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 146 (0x92) + // Code size 147 (0x93) .maxstack 4 .locals init (int V_0, System.Span V_1, int V_2, System.Collections.Generic.List V_3, - System.Span V_4) + System.Span V_4, + System.Span V_5) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 @@ -35928,27 +35937,27 @@ .locals init (int V_0, IL_0055: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" IL_005a: ldloc.3 IL_005b: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" - IL_0060: stloc.1 - IL_0061: ldc.i4.0 - IL_0062: stloc.0 - IL_0063: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" - IL_0068: stloc.s V_4 - IL_006a: ldloca.s V_4 - IL_006c: ldloca.s V_1 - IL_006e: ldloc.0 - IL_006f: ldloca.s V_4 - IL_0071: call "int System.Span.Length.get" - IL_0076: call "System.Span System.Span.Slice(int, int)" - IL_007b: call "void System.Span.CopyTo(System.Span)" - IL_0080: ldloc.0 - IL_0081: ldloca.s V_4 - IL_0083: call "int System.Span.Length.get" - IL_0088: add - IL_0089: stloc.0 - IL_008a: ldloc.3 - IL_008b: ldc.i4.0 - IL_008c: call "void CollectionExtensions.Report(object, bool)" - IL_0091: ret + IL_0060: stloc.s V_4 + IL_0062: ldc.i4.0 + IL_0063: stloc.0 + IL_0064: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" + IL_0069: stloc.s V_5 + IL_006b: ldloca.s V_5 + IL_006d: ldloca.s V_4 + IL_006f: ldloc.0 + IL_0070: ldloca.s V_5 + IL_0072: call "int System.Span.Length.get" + IL_0077: call "System.Span System.Span.Slice(int, int)" + IL_007c: call "void System.Span.CopyTo(System.Span)" + IL_0081: ldloc.0 + IL_0082: ldloca.s V_5 + IL_0084: call "int System.Span.Length.get" + IL_0089: add + IL_008a: stloc.0 + IL_008b: ldloc.3 + IL_008c: ldc.i4.0 + IL_008d: call "void CollectionExtensions.Report(object, bool)" + IL_0092: ret } """); } @@ -37859,15 +37868,16 @@ static void Main() // Ideally we'd like to be able to use *both* something like AddRange, *and* AsSpan/CopyTo/etc. while building the same target collection verifier.VerifyIL("C.Main", """ { - // Code size 181 (0xb5) + // Code size 182 (0xb6) .maxstack 3 .locals init (int V_0, System.Span V_1, int V_2, System.Collections.Generic.ICollection V_3, System.Collections.Generic.List V_4, - System.Collections.Generic.IEnumerator V_5, - int V_6) + System.Span V_5, + System.Collections.Generic.IEnumerator V_6, + int V_7) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 @@ -37920,49 +37930,49 @@ .locals init (int V_0, IL_005a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount(System.Collections.Generic.List, int)" IL_005f: ldloc.s V_4 IL_0061: call "System.Span System.Runtime.InteropServices.CollectionsMarshal.AsSpan(System.Collections.Generic.List)" - IL_0066: stloc.1 - IL_0067: ldc.i4.0 - IL_0068: stloc.0 - IL_0069: ldloc.3 - IL_006a: callvirt "System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator()" - IL_006f: stloc.s V_5 + IL_0066: stloc.s V_5 + IL_0068: ldc.i4.0 + IL_0069: stloc.0 + IL_006a: ldloc.3 + IL_006b: callvirt "System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator()" + IL_0070: stloc.s V_6 .try { - IL_0071: br.s IL_008b - IL_0073: ldloc.s V_5 - IL_0075: callvirt "int System.Collections.Generic.IEnumerator.Current.get" - IL_007a: stloc.s V_6 - IL_007c: ldloca.s V_1 - IL_007e: ldloc.0 - IL_007f: call "ref int System.Span.this[int].get" - IL_0084: ldloc.s V_6 - IL_0086: stind.i4 - IL_0087: ldloc.0 - IL_0088: ldc.i4.1 - IL_0089: add - IL_008a: stloc.0 - IL_008b: ldloc.s V_5 - IL_008d: callvirt "bool System.Collections.IEnumerator.MoveNext()" - IL_0092: brtrue.s IL_0073 - IL_0094: leave.s IL_00a2 + IL_0072: br.s IL_008c + IL_0074: ldloc.s V_6 + IL_0076: callvirt "int System.Collections.Generic.IEnumerator.Current.get" + IL_007b: stloc.s V_7 + IL_007d: ldloca.s V_5 + IL_007f: ldloc.0 + IL_0080: call "ref int System.Span.this[int].get" + IL_0085: ldloc.s V_7 + IL_0087: stind.i4 + IL_0088: ldloc.0 + IL_0089: ldc.i4.1 + IL_008a: add + IL_008b: stloc.0 + IL_008c: ldloc.s V_6 + IL_008e: callvirt "bool System.Collections.IEnumerator.MoveNext()" + IL_0093: brtrue.s IL_0074 + IL_0095: leave.s IL_00a3 } finally { - IL_0096: ldloc.s V_5 - IL_0098: brfalse.s IL_00a1 - IL_009a: ldloc.s V_5 - IL_009c: callvirt "void System.IDisposable.Dispose()" - IL_00a1: endfinally + IL_0097: ldloc.s V_6 + IL_0099: brfalse.s IL_00a2 + IL_009b: ldloc.s V_6 + IL_009d: callvirt "void System.IDisposable.Dispose()" + IL_00a2: endfinally } - IL_00a2: ldloca.s V_1 - IL_00a4: ldloc.0 - IL_00a5: call "ref int System.Span.this[int].get" - IL_00aa: ldc.i4.4 - IL_00ab: stind.i4 - IL_00ac: ldloc.s V_4 - IL_00ae: ldc.i4.0 - IL_00af: call "void CollectionExtensions.Report(object, bool)" - IL_00b4: ret + IL_00a3: ldloca.s V_5 + IL_00a5: ldloc.0 + IL_00a6: call "ref int System.Span.this[int].get" + IL_00ab: ldc.i4.4 + IL_00ac: stind.i4 + IL_00ad: ldloc.s V_4 + IL_00af: ldc.i4.0 + IL_00b0: call "void CollectionExtensions.Report(object, bool)" + IL_00b5: ret } """); } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs index 027e23174b049..a9621b5999619 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs @@ -7339,13 +7339,14 @@ static async Task FromResult(T r) verifier.VerifyIL("Program.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { - // Code size 208 (0xd0) + // Code size 209 (0xd1) .maxstack 3 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter V_2, System.Span V_3, - System.Exception V_4) + System.Span V_4, + System.Exception V_5) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.d__2.<>1__state"" IL_0006: stloc.0 @@ -7377,7 +7378,7 @@ .locals init (int V_0, IL_0041: ldloca.s V_2 IL_0043: ldarg.0 IL_0044: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Program.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.d__2)"" - IL_0049: leave IL_00cf + IL_0049: leave IL_00d0 IL_004e: ldarg.0 IL_004f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter Program.d__2.<>u__1"" IL_0054: stloc.2 @@ -7402,36 +7403,36 @@ .locals init (int V_0, IL_0087: ldc.i4.0 IL_0088: ldloc.1 IL_0089: call ""System.Span System.Span.Slice(int, int)"" - IL_008e: stloc.3 - IL_008f: ldloca.s V_3 - IL_0091: ldc.i4.0 - IL_0092: call ""ref int System.Span.this[int].get"" - IL_0097: ldc.i4.s 111 - IL_0099: stind.i4 - IL_009a: ldarg.0 - IL_009b: ldnull - IL_009c: stfld ""C Program.d__2.<>7__wrap1"" - IL_00a1: leave.s IL_00bc + IL_008e: stloc.s V_4 + IL_0090: ldloca.s V_4 + IL_0092: ldc.i4.0 + IL_0093: call ""ref int System.Span.this[int].get"" + IL_0098: ldc.i4.s 111 + IL_009a: stind.i4 + IL_009b: ldarg.0 + IL_009c: ldnull + IL_009d: stfld ""C Program.d__2.<>7__wrap1"" + IL_00a2: leave.s IL_00bd } catch System.Exception { - IL_00a3: stloc.s V_4 - IL_00a5: ldarg.0 - IL_00a6: ldc.i4.s -2 - IL_00a8: stfld ""int Program.d__2.<>1__state"" - IL_00ad: ldarg.0 - IL_00ae: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__2.<>t__builder"" - IL_00b3: ldloc.s V_4 - IL_00b5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_00ba: leave.s IL_00cf + IL_00a4: stloc.s V_5 + IL_00a6: ldarg.0 + IL_00a7: ldc.i4.s -2 + IL_00a9: stfld ""int Program.d__2.<>1__state"" + IL_00ae: ldarg.0 + IL_00af: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__2.<>t__builder"" + IL_00b4: ldloc.s V_5 + IL_00b6: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_00bb: leave.s IL_00d0 } - IL_00bc: ldarg.0 - IL_00bd: ldc.i4.s -2 - IL_00bf: stfld ""int Program.d__2.<>1__state"" - IL_00c4: ldarg.0 - IL_00c5: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__2.<>t__builder"" - IL_00ca: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_00cf: ret + IL_00bd: ldarg.0 + IL_00be: ldc.i4.s -2 + IL_00c0: stfld ""int Program.d__2.<>1__state"" + IL_00c5: ldarg.0 + IL_00c6: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__2.<>t__builder"" + IL_00cb: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00d0: ret } "); } @@ -7482,7 +7483,8 @@ .locals init (int V_0, int V_2, System.Runtime.CompilerServices.TaskAwaiter V_3, System.Span V_4, - System.Exception V_5) + System.Span V_5, + System.Exception V_6) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.d__2.<>1__state"" IL_0006: stloc.0 @@ -7547,8 +7549,8 @@ .locals init (int V_0, IL_0098: ldloc.2 IL_0099: sub IL_009a: call ""System.Span System.Span.Slice(int, int)"" - IL_009f: stloc.s V_4 - IL_00a1: ldloca.s V_4 + IL_009f: stloc.s V_5 + IL_00a1: ldloca.s V_5 IL_00a3: ldc.i4.0 IL_00a4: call ""ref int System.Span.this[int].get"" IL_00a9: ldc.i4.s 111 @@ -7560,13 +7562,13 @@ .locals init (int V_0, } catch System.Exception { - IL_00b5: stloc.s V_5 + IL_00b5: stloc.s V_6 IL_00b7: ldarg.0 IL_00b8: ldc.i4.s -2 IL_00ba: stfld ""int Program.d__2.<>1__state"" IL_00bf: ldarg.0 IL_00c0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__2.<>t__builder"" - IL_00c5: ldloc.s V_5 + IL_00c5: ldloc.s V_6 IL_00c7: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_00cc: leave.s IL_00e1 } @@ -7630,7 +7632,8 @@ .locals init (int V_0, System.Runtime.CompilerServices.TaskAwaiter V_5, System.Index V_6, System.Span V_7, - System.Exception V_8) + System.Span V_8, + System.Exception V_9) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.d__2.<>1__state"" IL_0006: stloc.0 @@ -7711,8 +7714,8 @@ .locals init (int V_0, IL_00cc: ldloc.3 IL_00cd: ldloc.s V_4 IL_00cf: call ""System.Span System.Span.Slice(int, int)"" - IL_00d4: stloc.s V_7 - IL_00d6: ldloca.s V_7 + IL_00d4: stloc.s V_8 + IL_00d6: ldloca.s V_8 IL_00d8: ldc.i4.0 IL_00d9: call ""ref int System.Span.this[int].get"" IL_00de: ldc.i4.s 111 @@ -7724,13 +7727,13 @@ .locals init (int V_0, } catch System.Exception { - IL_00ea: stloc.s V_8 + IL_00ea: stloc.s V_9 IL_00ec: ldarg.0 IL_00ed: ldc.i4.s -2 IL_00ef: stfld ""int Program.d__2.<>1__state"" IL_00f4: ldarg.0 IL_00f5: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__2.<>t__builder"" - IL_00fa: ldloc.s V_8 + IL_00fa: ldloc.s V_9 IL_00fc: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_0101: leave.s IL_0116 } @@ -7795,7 +7798,8 @@ .locals init (int V_0, int V_2, System.Runtime.CompilerServices.TaskAwaiter V_3, System.Span V_4, - System.Exception V_5) + System.Span V_5, + System.Exception V_6) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.d__2.<>1__state"" IL_0006: stloc.0 @@ -7872,8 +7876,8 @@ .locals init (int V_0, IL_00c5: ldarg.0 IL_00c6: ldfld ""int Program.d__2.<>7__wrap2"" IL_00cb: call ""System.Span System.Span.Slice(int, int)"" - IL_00d0: stloc.s V_4 - IL_00d2: ldloca.s V_4 + IL_00d0: stloc.s V_5 + IL_00d2: ldloca.s V_5 IL_00d4: ldarg.0 IL_00d5: ldfld ""int Program.d__2.<>7__wrap3"" IL_00da: call ""ref int System.Span.this[int].get"" @@ -7886,13 +7890,13 @@ .locals init (int V_0, } catch System.Exception { - IL_00ea: stloc.s V_5 + IL_00ea: stloc.s V_6 IL_00ec: ldarg.0 IL_00ed: ldc.i4.s -2 IL_00ef: stfld ""int Program.d__2.<>1__state"" IL_00f4: ldarg.0 IL_00f5: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__2.<>t__builder"" - IL_00fa: ldloc.s V_5 + IL_00fa: ldloc.s V_6 IL_00fc: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_0101: leave.s IL_0116 } @@ -7961,7 +7965,8 @@ .locals init (int V_0, System.Index V_6, System.Runtime.CompilerServices.TaskAwaiter V_7, System.ReadOnlySpan V_8, - System.Exception V_9) + System.ReadOnlySpan V_9, + System.Exception V_10) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.d__1.<>1__state"" IL_0006: stloc.0 @@ -8051,8 +8056,8 @@ .locals init (int V_0, IL_00ee: ldarg.0 IL_00ef: ldfld ""int Program.d__1.<>7__wrap2"" IL_00f4: call ""System.ReadOnlySpan System.ReadOnlySpan.Slice(int, int)"" - IL_00f9: stloc.s V_8 - IL_00fb: ldloca.s V_8 + IL_00f9: stloc.s V_9 + IL_00fb: ldloca.s V_9 IL_00fd: ldloc.s V_5 IL_00ff: call ""ref readonly int System.ReadOnlySpan.this[int].get"" IL_0104: ldind.i4 @@ -8061,13 +8066,13 @@ .locals init (int V_0, } catch System.Exception { - IL_0108: stloc.s V_9 + IL_0108: stloc.s V_10 IL_010a: ldarg.0 IL_010b: ldc.i4.s -2 IL_010d: stfld ""int Program.d__1.<>1__state"" IL_0112: ldarg.0 IL_0113: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.d__1.<>t__builder"" - IL_0118: ldloc.s V_9 + IL_0118: ldloc.s V_10 IL_011a: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_011f: leave.s IL_0135 } @@ -8813,7 +8818,8 @@ static int M4(in int x, Buffer10 y) // Code size 32 (0x20) .maxstack 2 .locals init (Buffer10 V_0, - int V_1) + int V_1, + Buffer10 V_2) IL_0000: call ""Buffer10 Program.M3()"" IL_0005: stloc.0 IL_0006: ldloca.s V_0 @@ -8821,9 +8827,9 @@ .locals init (Buffer10 V_0, IL_000d: ldind.i4 IL_000e: stloc.1 IL_000f: ldloca.s V_1 - IL_0011: ldloca.s V_0 + IL_0011: ldloca.s V_2 IL_0013: initobj ""Buffer10"" - IL_0019: ldloc.0 + IL_0019: ldloc.2 IL_001a: call ""int Program.M4(in int, Buffer10)"" IL_001f: ret } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 1c2864d66eac5..7ae0f73ffafb3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -3822,7 +3822,8 @@ public void NestedInterpolatedStrings_02(string expression) .maxstack 4 .locals init (int V_0, //i1 int V_1, //i2 - System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.2 @@ -3836,14 +3837,14 @@ .locals init (int V_0, //i1 IL_0010: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" IL_0015: ldloca.s V_2 IL_0017: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" - IL_001c: ldloca.s V_2 + IL_001c: ldloca.s V_3 IL_001e: ldc.i4.0 IL_001f: ldc.i4.1 IL_0020: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" - IL_0025: ldloca.s V_2 + IL_0025: ldloca.s V_3 IL_0027: ldloc.1 IL_0028: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" - IL_002d: ldloca.s V_2 + IL_002d: ldloca.s V_3 IL_002f: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0034: call ""string string.Concat(string, string)"" IL_0039: call ""void System.Console.WriteLine(string)"" @@ -10151,14 +10152,15 @@ Creating DummyHandler from StructLogger#2 verifier.VerifyIL("", @" { - // Code size 175 (0xaf) + // Code size 177 (0xb1) .maxstack 4 .locals init (int V_0, //i StructLogger V_1, //s StructLogger V_2, DummyHandler V_3, bool V_4, - StructLogger& V_5) + StructLogger& V_5, + DummyHandler V_6) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_2 @@ -10207,32 +10209,32 @@ .locals init (int V_0, //i IL_0064: ldobj ""StructLogger"" IL_0069: ldloca.s V_4 IL_006b: newobj ""DummyHandler..ctor(int, int, StructLogger, out bool)"" - IL_0070: stloc.3 - IL_0071: ldloc.s V_4 - IL_0073: brfalse.s IL_008f - IL_0075: ldloca.s V_3 - IL_0077: ldstr ""log:"" - IL_007c: call ""void DummyHandler.AppendLiteral(string)"" - IL_0081: nop - IL_0082: ldloca.s V_3 - IL_0084: ldloc.0 - IL_0085: dup - IL_0086: ldc.i4.1 - IL_0087: add - IL_0088: stloc.0 - IL_0089: call ""void DummyHandler.AppendFormatted(int)"" - IL_008e: nop - IL_008f: ldloc.s V_5 - IL_0091: ldloc.3 - IL_0092: call ""void StructLogger.Log(DummyHandler)"" - IL_0097: nop - IL_0098: ldstr ""(2) i={0}"" - IL_009d: ldloc.0 - IL_009e: box ""int"" - IL_00a3: call ""string string.Format(string, object)"" - IL_00a8: call ""void System.Console.WriteLine(string)"" - IL_00ad: nop - IL_00ae: ret + IL_0070: stloc.s V_6 + IL_0072: ldloc.s V_4 + IL_0074: brfalse.s IL_0090 + IL_0076: ldloca.s V_6 + IL_0078: ldstr ""log:"" + IL_007d: call ""void DummyHandler.AppendLiteral(string)"" + IL_0082: nop + IL_0083: ldloca.s V_6 + IL_0085: ldloc.0 + IL_0086: dup + IL_0087: ldc.i4.1 + IL_0088: add + IL_0089: stloc.0 + IL_008a: call ""void DummyHandler.AppendFormatted(int)"" + IL_008f: nop + IL_0090: ldloc.s V_5 + IL_0092: ldloc.s V_6 + IL_0094: call ""void StructLogger.Log(DummyHandler)"" + IL_0099: nop + IL_009a: ldstr ""(2) i={0}"" + IL_009f: ldloc.0 + IL_00a0: box ""int"" + IL_00a5: call ""string string.Format(string, object)"" + IL_00aa: call ""void System.Console.WriteLine(string)"" + IL_00af: nop + IL_00b0: ret } "); } @@ -16800,13 +16802,14 @@ public void ParenthesizedAdditiveExpression_04() verifier.VerifyIL("Program.<
$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { - // Code size 244 (0xf4) + // Code size 245 (0xf5) .maxstack 4 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter V_2, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, - System.Exception V_4) + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4, + System.Exception V_5) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<
$>d__0.<>1__state"" IL_0006: stloc.0 @@ -16839,7 +16842,7 @@ .locals init (int V_0, IL_0042: ldloca.s V_2 IL_0044: ldarg.0 IL_0045: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Program.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.<
$>d__0)"" - IL_004a: leave IL_00f3 + IL_004a: leave IL_00f4 IL_004f: ldarg.0 IL_0050: ldfld ""System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1"" IL_0055: stloc.2 @@ -16871,36 +16874,36 @@ .locals init (int V_0, IL_009f: ldc.i4.0 IL_00a0: ldc.i4.1 IL_00a1: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" - IL_00a6: stloc.3 - IL_00a7: ldloca.s V_3 - IL_00a9: ldarg.0 - IL_00aa: ldfld ""int Program.<
$>d__0.5__3"" - IL_00af: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" - IL_00b4: ldloca.s V_3 - IL_00b6: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" - IL_00bb: call ""string string.Concat(string, string, string)"" - IL_00c0: call ""void System.Console.WriteLine(string)"" - IL_00c5: leave.s IL_00e0 + IL_00a6: stloc.s V_4 + IL_00a8: ldloca.s V_4 + IL_00aa: ldarg.0 + IL_00ab: ldfld ""int Program.<
$>d__0.5__3"" + IL_00b0: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_00b5: ldloca.s V_4 + IL_00b7: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_00bc: call ""string string.Concat(string, string, string)"" + IL_00c1: call ""void System.Console.WriteLine(string)"" + IL_00c6: leave.s IL_00e1 } catch System.Exception { - IL_00c7: stloc.s V_4 - IL_00c9: ldarg.0 - IL_00ca: ldc.i4.s -2 - IL_00cc: stfld ""int Program.<
$>d__0.<>1__state"" - IL_00d1: ldarg.0 - IL_00d2: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" - IL_00d7: ldloc.s V_4 - IL_00d9: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_00de: leave.s IL_00f3 + IL_00c8: stloc.s V_5 + IL_00ca: ldarg.0 + IL_00cb: ldc.i4.s -2 + IL_00cd: stfld ""int Program.<
$>d__0.<>1__state"" + IL_00d2: ldarg.0 + IL_00d3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" + IL_00d8: ldloc.s V_5 + IL_00da: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_00df: leave.s IL_00f4 } - IL_00e0: ldarg.0 - IL_00e1: ldc.i4.s -2 - IL_00e3: stfld ""int Program.<
$>d__0.<>1__state"" - IL_00e8: ldarg.0 - IL_00e9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" - IL_00ee: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_00f3: ret + IL_00e1: ldarg.0 + IL_00e2: ldc.i4.s -2 + IL_00e4: stfld ""int Program.<
$>d__0.<>1__state"" + IL_00e9: ldarg.0 + IL_00ea: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" + IL_00ef: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00f4: ret }"); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs index 9770e5eb3930b..81c060e72a02b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs @@ -2223,7 +2223,8 @@ public void NestedInterpolatedStrings_02(string expression) .maxstack 4 .locals init (int V_0, //i1 int V_1, //i2 - System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.2 @@ -2237,14 +2238,14 @@ .locals init (int V_0, //i1 IL_0010: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" IL_0015: ldloca.s V_2 IL_0017: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" - IL_001c: ldloca.s V_2 + IL_001c: ldloca.s V_3 IL_001e: ldc.i4.0 IL_001f: ldc.i4.1 IL_0020: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" - IL_0025: ldloca.s V_2 + IL_0025: ldloca.s V_3 IL_0027: ldloc.1 IL_0028: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" - IL_002d: ldloca.s V_2 + IL_002d: ldloca.s V_3 IL_002f: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" IL_0034: call ""string string.Concat(string, string)"" IL_0039: call ""void System.Console.WriteLine(string)"" @@ -12228,13 +12229,14 @@ public void ParenthesizedAdditiveExpression_04() verifier.VerifyIL("Program.<
$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { - // Code size 244 (0xf4) + // Code size 245 (0xf5) .maxstack 4 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter V_2, System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, - System.Exception V_4) + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4, + System.Exception V_5) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<
$>d__0.<>1__state"" IL_0006: stloc.0 @@ -12267,7 +12269,7 @@ .locals init (int V_0, IL_0042: ldloca.s V_2 IL_0044: ldarg.0 IL_0045: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Program.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.<
$>d__0)"" - IL_004a: leave IL_00f3 + IL_004a: leave IL_00f4 IL_004f: ldarg.0 IL_0050: ldfld ""System.Runtime.CompilerServices.TaskAwaiter Program.<
$>d__0.<>u__1"" IL_0055: stloc.2 @@ -12299,36 +12301,36 @@ .locals init (int V_0, IL_009f: ldc.i4.0 IL_00a0: ldc.i4.1 IL_00a1: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" - IL_00a6: stloc.3 - IL_00a7: ldloca.s V_3 - IL_00a9: ldarg.0 - IL_00aa: ldfld ""int Program.<
$>d__0.5__3"" - IL_00af: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" - IL_00b4: ldloca.s V_3 - IL_00b6: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" - IL_00bb: call ""string string.Concat(string, string, string)"" - IL_00c0: call ""void System.Console.WriteLine(string)"" - IL_00c5: leave.s IL_00e0 + IL_00a6: stloc.s V_4 + IL_00a8: ldloca.s V_4 + IL_00aa: ldarg.0 + IL_00ab: ldfld ""int Program.<
$>d__0.5__3"" + IL_00b0: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_00b5: ldloca.s V_4 + IL_00b7: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_00bc: call ""string string.Concat(string, string, string)"" + IL_00c1: call ""void System.Console.WriteLine(string)"" + IL_00c6: leave.s IL_00e1 } catch System.Exception { - IL_00c7: stloc.s V_4 - IL_00c9: ldarg.0 - IL_00ca: ldc.i4.s -2 - IL_00cc: stfld ""int Program.<
$>d__0.<>1__state"" - IL_00d1: ldarg.0 - IL_00d2: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" - IL_00d7: ldloc.s V_4 - IL_00d9: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_00de: leave.s IL_00f3 + IL_00c8: stloc.s V_5 + IL_00ca: ldarg.0 + IL_00cb: ldc.i4.s -2 + IL_00cd: stfld ""int Program.<
$>d__0.<>1__state"" + IL_00d2: ldarg.0 + IL_00d3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" + IL_00d8: ldloc.s V_5 + IL_00da: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_00df: leave.s IL_00f4 } - IL_00e0: ldarg.0 - IL_00e1: ldc.i4.s -2 - IL_00e3: stfld ""int Program.<
$>d__0.<>1__state"" - IL_00e8: ldarg.0 - IL_00e9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" - IL_00ee: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_00f3: ret + IL_00e1: ldarg.0 + IL_00e2: ldc.i4.s -2 + IL_00e4: stfld ""int Program.<
$>d__0.<>1__state"" + IL_00e9: ldarg.0 + IL_00ea: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder"" + IL_00ef: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00f4: ret }"); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 0b3a373b843a2..4ab1f964b7031 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -11009,6 +11009,1099 @@ static R F2() Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(18, 22)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes() + { + var source = """ + var r1 = new R(111); + var r2 = new R(222); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + ref struct R(in int x) + { + public ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics() + // Needs two int temps. + .VerifyIL("", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (R V_0, //r2 + int V_1, + int V_2) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: newobj "R..ctor(in int)" + IL_000a: ldc.i4 0xde + IL_000f: stloc.2 + IL_0010: ldloca.s V_2 + IL_0012: newobj "R..ctor(in int)" + IL_0017: stloc.0 + IL_0018: ldfld "ref readonly int R.F" + IL_001d: ldind.i4 + IL_001e: ldloc.0 + IL_001f: ldfld "ref readonly int R.F" + IL_0024: ldind.i4 + IL_0025: call "void Program.<
$>g__Report|0_0(int, int)" + IL_002a: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_NonRefStruct() + { + var source = """ + var r1 = new R(111); + var r2 = new R(222); + + struct R(in int x) + { + public readonly int F = x; + } + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int temp is enough. + .VerifyIL("", """ + { + // Code size 26 (0x1a) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: newobj "R..ctor(in int)" + IL_000a: pop + IL_000b: ldc.i4 0xde + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: newobj "R..ctor(in int)" + IL_0018: pop + IL_0019: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_UnusedResult() + { + var source = """ + new R(111); + new R(222); + + ref struct R + { + public R(in int x) { } + } + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int temp is enough. + .VerifyIL("", """ + { + // Code size 26 (0x1a) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: newobj "R..ctor(in int)" + IL_000a: pop + IL_000b: ldc.i4 0xde + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: newobj "R..ctor(in int)" + IL_0018: pop + IL_0019: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_ScopedParameter() + { + var source = """ + var r1 = new R(111); + var r2 = new R(222); + Use(r1, r2); + + static void Use(R x, R y) { } + + ref struct R + { + public R(scoped in int x) { } + } + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int temp is enough. + .VerifyIL("", """ + { + // Code size 31 (0x1f) + .maxstack 2 + .locals init (R V_0, //r2 + int V_1) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: newobj "R..ctor(scoped in int)" + IL_000a: ldc.i4 0xde + IL_000f: stloc.1 + IL_0010: ldloca.s V_1 + IL_0012: newobj "R..ctor(scoped in int)" + IL_0017: stloc.0 + IL_0018: ldloc.0 + IL_0019: call "void Program.<
$>g__Use|0_0(R, R)" + IL_001e: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_NestedBlock() + { + var source = """ + { + var r1 = new R(111); + var r2 = new R(222); + Report(r1.F, r2.F); + } + { + var r1 = new R(333); + var r2 = new R(444); + Report(r1.F, r2.F); + } + + static void Report(int x, int y) => System.Console.Write($"{x} {y} "); + + ref struct R(in int x) + { + public ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222 333 444"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics() + // Two int and two R temps would be enough, but the emit layer currently does not take blocks into account. + .VerifyIL("", """ + { + // Code size 90 (0x5a) + .maxstack 2 + .locals init (R V_0, //r2 + int V_1, + int V_2, + R V_3, //r2 + int V_4, + int V_5) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: newobj "R..ctor(in int)" + IL_000a: ldc.i4 0xde + IL_000f: stloc.2 + IL_0010: ldloca.s V_2 + IL_0012: newobj "R..ctor(in int)" + IL_0017: stloc.0 + IL_0018: ldfld "ref readonly int R.F" + IL_001d: ldind.i4 + IL_001e: ldloc.0 + IL_001f: ldfld "ref readonly int R.F" + IL_0024: ldind.i4 + IL_0025: call "void Program.<
$>g__Report|0_0(int, int)" + IL_002a: ldc.i4 0x14d + IL_002f: stloc.s V_4 + IL_0031: ldloca.s V_4 + IL_0033: newobj "R..ctor(in int)" + IL_0038: ldc.i4 0x1bc + IL_003d: stloc.s V_5 + IL_003f: ldloca.s V_5 + IL_0041: newobj "R..ctor(in int)" + IL_0046: stloc.3 + IL_0047: ldfld "ref readonly int R.F" + IL_004c: ldind.i4 + IL_004d: ldloc.3 + IL_004e: ldfld "ref readonly int R.F" + IL_0053: ldind.i4 + IL_0054: call "void Program.<
$>g__Report|0_0(int, int)" + IL_0059: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_ComCall() + { + var source = """ + using System.Runtime.InteropServices; + + I i = new C(); + i.M(111); + i.M(222); + + [ComImport, Guid("96A2DE64-6D44-4DA5-BBA4-25F5F07E0E6B")] + interface I + { + void M(ref int i); + } + + class C : I + { + void I.M(ref int i) { } + } + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int temp is enough. + .VerifyIL("", """ + { + // Code size 30 (0x1e) + .maxstack 3 + .locals init (int V_0) + IL_0000: newobj "C..ctor()" + IL_0005: dup + IL_0006: ldc.i4.s 111 + IL_0008: stloc.0 + IL_0009: ldloca.s V_0 + IL_000b: callvirt "void I.M(ref int)" + IL_0010: ldc.i4 0xde + IL_0015: stloc.0 + IL_0016: ldloca.s V_0 + IL_0018: callvirt "void I.M(ref int)" + IL_001d: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_ComCall() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + using System.Runtime.InteropServices; + + I i = new C(); + var r1 = i.M(111); + var r2 = i.M(222); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + [ComImport, Guid("96A2DE64-6D44-4DA5-BBA4-25F5F07E0E6B")] + interface I + { + R M([UnscopedRef] ref int i); + } + + class C : I + { + R I.M([UnscopedRef] ref int i) + { + var r = new R(); + r.F = ref i; + return r; + } + } + + ref struct R + { + public ref int F; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics() + // Needs two int temps. + .VerifyIL("", """ + { + // Code size 51 (0x33) + .maxstack 3 + .locals init (R V_0, //r1 + R V_1, //r2 + int V_2, + int V_3) + IL_0000: newobj "C..ctor()" + IL_0005: dup + IL_0006: ldc.i4.s 111 + IL_0008: stloc.2 + IL_0009: ldloca.s V_2 + IL_000b: callvirt "R I.M(ref int)" + IL_0010: stloc.0 + IL_0011: ldc.i4 0xde + IL_0016: stloc.3 + IL_0017: ldloca.s V_3 + IL_0019: callvirt "R I.M(ref int)" + IL_001e: stloc.1 + IL_001f: ldloc.0 + IL_0020: ldfld "ref int R.F" + IL_0025: ldind.i4 + IL_0026: ldloc.1 + IL_0027: ldfld "ref int R.F" + IL_002c: ldind.i4 + IL_002d: call "void Program.<
$>g__Report|0_0(int, int)" + IL_0032: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_NoTemp() + { + var source = """ + var x1 = 111; + var r1 = new R(x1); + var x2 = 222; + var r2 = new R(x2); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + ref struct R(in int x) + { + public ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics() + // No int temps needed. + .VerifyIL("", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (int V_0, //x1 + int V_1, //x2 + R V_2) //r2 + IL_0000: ldc.i4.s 111 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: newobj "R..ctor(in int)" + IL_000a: ldc.i4 0xde + IL_000f: stloc.1 + IL_0010: ldloca.s V_1 + IL_0012: newobj "R..ctor(in int)" + IL_0017: stloc.2 + IL_0018: ldfld "ref readonly int R.F" + IL_001d: ldind.i4 + IL_001e: ldloc.2 + IL_001f: ldfld "ref readonly int R.F" + IL_0024: ldind.i4 + IL_0025: call "void Program.<
$>g__Report|0_0(int, int)" + IL_002a: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_RValue() + { + var source = """ + var r1 = new R(F1()); + var r2 = new R(F2()); + Report(r1.F, r2.F); + + static int F1() => 111; + static int F2() => 222; + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + ref struct R(in int x) + { + public ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics() + // Needs two int temps. + .VerifyIL("", """ + { + // Code size 46 (0x2e) + .maxstack 2 + .locals init (R V_0, //r2 + int V_1, + int V_2) + IL_0000: call "int Program.<
$>g__F1|0_0()" + IL_0005: stloc.1 + IL_0006: ldloca.s V_1 + IL_0008: newobj "R..ctor(in int)" + IL_000d: call "int Program.<
$>g__F2|0_1()" + IL_0012: stloc.2 + IL_0013: ldloca.s V_2 + IL_0015: newobj "R..ctor(in int)" + IL_001a: stloc.0 + IL_001b: ldfld "ref readonly int R.F" + IL_0020: ldind.i4 + IL_0021: ldloc.0 + IL_0022: ldfld "ref readonly int R.F" + IL_0027: ldind.i4 + IL_0028: call "void Program.<
$>g__Report|0_2(int, int)" + IL_002d: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_Assignment() + { + var source = """ + var r1 = new R(100); + r1 = new R(101); + var r2 = new R(200); + r2 = new R(201); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + ref struct R(in int x) + { + public ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("101 201"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics() + // Needs at least two int temps. + .VerifyIL("", """ + { + // Code size 68 (0x44) + .maxstack 2 + .locals init (R V_0, //r2 + int V_1, + int V_2, + int V_3) + IL_0000: ldc.i4.s 100 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: newobj "R..ctor(in int)" + IL_000a: pop + IL_000b: ldc.i4.s 101 + IL_000d: stloc.1 + IL_000e: ldloca.s V_1 + IL_0010: newobj "R..ctor(in int)" + IL_0015: ldc.i4 0xc8 + IL_001a: stloc.2 + IL_001b: ldloca.s V_2 + IL_001d: newobj "R..ctor(in int)" + IL_0022: stloc.0 + IL_0023: ldc.i4 0xc9 + IL_0028: stloc.3 + IL_0029: ldloca.s V_3 + IL_002b: newobj "R..ctor(in int)" + IL_0030: stloc.0 + IL_0031: ldfld "ref readonly int R.F" + IL_0036: ldind.i4 + IL_0037: ldloc.0 + IL_0038: ldfld "ref readonly int R.F" + IL_003d: ldind.i4 + IL_003e: call "void Program.<
$>g__Report|0_0(int, int)" + IL_0043: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_PatternMatch() + { + var source = """ + if (new R(111) is { } r1 && new R(222) is { } r2) + { + Report(r1.F, r2.F); + } + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + ref struct R(in int x) + { + public ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics() + // Needs two int temps. + .VerifyIL("", """ + { + // Code size 45 (0x2d) + .maxstack 2 + .locals init (R V_0, //r1 + R V_1, //r2 + int V_2, + int V_3) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.2 + IL_0003: ldloca.s V_2 + IL_0005: newobj "R..ctor(in int)" + IL_000a: stloc.0 + IL_000b: ldc.i4 0xde + IL_0010: stloc.3 + IL_0011: ldloca.s V_3 + IL_0013: newobj "R..ctor(in int)" + IL_0018: stloc.1 + IL_0019: ldloc.0 + IL_001a: ldfld "ref readonly int R.F" + IL_001f: ldind.i4 + IL_0020: ldloc.1 + IL_0021: ldfld "ref readonly int R.F" + IL_0026: ldind.i4 + IL_0027: call "void Program.<
$>g__Report|0_0(int, int)" + IL_002c: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_Initializer() + { + var source = """ + var r1 = new R() { F = ref M(111) }; + var r2 = new R() { F = ref M(222) }; + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + static ref readonly int M(in int x) => ref x; + + ref struct R + { + public ref readonly int F; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_ViaOutParameter( + [CombinatorialValues("", "scoped")] string scoped, + [CombinatorialValues("", "readonly")] string ro) + { + var source = $$""" + scoped R r1, r2; + M(111, out r1); + M(222, out r2); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + static void M(in int x, {{scoped}} out R r) => r = new R(x); + + {{ro}} ref struct R(in int x) + { + public {{ro}} ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_ViaOutParameter() + { + var source = """ + scoped R r1, r2; + M(111, out r1); + M(222, out r2); + + static void M(scoped in int x, scoped out R r) { } + + ref struct R; + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int temp would be enough, but currently the scope difference between input/output is not recognized by the heuristic. + .VerifyIL("", """ + { + // Code size 28 (0x1c) + .maxstack 2 + .locals init (R V_0, //r1 + R V_1, //r2 + int V_2, + int V_3) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.2 + IL_0003: ldloca.s V_2 + IL_0005: ldloca.s V_0 + IL_0007: call "void Program.<
$>g__M|0_0(scoped in int, out R)" + IL_000c: ldc.i4 0xde + IL_0011: stloc.3 + IL_0012: ldloca.s V_3 + IL_0014: ldloca.s V_1 + IL_0016: call "void Program.<
$>g__M|0_0(scoped in int, out R)" + IL_001b: ret + } + """); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_ViaRefParameter( + [CombinatorialValues("", "scoped")] string scoped) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + scoped var r1 = new R(); + scoped var r2 = new R(); + M(111, ref r1); + M(222, ref r2); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + static void M([UnscopedRef] in int x, {{scoped}} ref R r) + { + r.F = ref x; + } + + ref struct R + { + public ref readonly int F; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_ViaRefParameter_Readonly( + [CombinatorialValues("", "scoped")] string scoped) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + scoped var r1 = new R(); + scoped var r2 = new R(); + M(111, ref r1); + M(222, ref r2); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + static void M([UnscopedRef] in int x, {{scoped}} ref R r) + { + r = new R(in x); + } + + readonly ref struct R(ref readonly int x) + { + public readonly ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_ViaRefParameter() + { + var source = """ + scoped R r1 = new(), r2 = new(); + M(111, ref r1); + M(222, ref r2); + + static void M(in int x, ref R r) { } + + ref struct R; + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int temp would be enough, but currently the scope difference between input/output is not recognized by the heuristic. + .VerifyIL("", """ + { + // Code size 44 (0x2c) + .maxstack 2 + .locals init (R V_0, //r1 + R V_1, //r2 + int V_2, + int V_3) + IL_0000: ldloca.s V_0 + IL_0002: initobj "R" + IL_0008: ldloca.s V_1 + IL_000a: initobj "R" + IL_0010: ldc.i4.s 111 + IL_0012: stloc.2 + IL_0013: ldloca.s V_2 + IL_0015: ldloca.s V_0 + IL_0017: call "void Program.<
$>g__M|0_0(in int, ref R)" + IL_001c: ldc.i4 0xde + IL_0021: stloc.3 + IL_0022: ldloca.s V_3 + IL_0024: ldloca.s V_1 + IL_0026: call "void Program.<
$>g__M|0_0(in int, ref R)" + IL_002b: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_ViaOutDiscard() + { + var source = """ + M(111, out _); + M(222, out _); + + static void M(in int x, out R r) => r = default; + + ref struct R; + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int and one R temp would be enough. But currently the emit layer does not see the argument is a discard. + .VerifyIL("", """ + { + // Code size 28 (0x1c) + .maxstack 2 + .locals init (R V_0, + int V_1, + R V_2, + int V_3) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: ldloca.s V_0 + IL_0007: call "void Program.<
$>g__M|0_0(in int, out R)" + IL_000c: ldc.i4 0xde + IL_0011: stloc.3 + IL_0012: ldloca.s V_3 + IL_0014: ldloca.s V_2 + IL_0016: call "void Program.<
$>g__M|0_0(in int, out R)" + IL_001b: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_Scoped() + { + var source = """ + var r1 = new R(111); + var r2 = new R(222); + + ref struct R + { + public R(scoped in int x) { } + } + """; + CompileAndVerify(source) + .VerifyDiagnostics() + // One int temp is enough. + .VerifyIL("", """ + { + // Code size 26 (0x1a) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldc.i4.s 111 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: newobj "R..ctor(scoped in int)" + IL_000a: pop + IL_000b: ldc.i4 0xde + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: newobj "R..ctor(scoped in int)" + IL_0018: pop + IL_0019: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_InstanceMethod() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + scoped var r1 = new R(); + scoped var r2 = new R(); + r1.Set(111); + r2.Set(222); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + ref struct R + { + public ref readonly int F; + + public void Set([UnscopedRef] in int x) => F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_InstanceMethod_Chained() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + scoped var r1 = new R(); + scoped var r2 = new R(); + new C().Set(111).To(ref r1); + new C().Set(222).To(ref r2); + Report(r1.F, r2.F); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + class C + { + public R Set([UnscopedRef] in int x) + { + var r = new R(); + r.F = ref x; + return r; + } + } + + ref struct R + { + public ref readonly int F; + public void To(ref R r) + { + r.F = ref F; + } + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_FunctionPointer() + { + var source = """ + unsafe + { + delegate* f = &M; + var r1 = f(111); + var r2 = f(222); + Report(r1.F, r2.F); + } + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + static R M(in int x) => new R(in x); + + ref struct R(ref readonly int x) + { + public ref readonly int F = ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + options: TestOptions.UnsafeReleaseExe, + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_RefAssignment() + { + var source = """ + ref readonly int x = ref 111.M(); + ref readonly int y = ref 222.M(); + + Report(x, y); + + static void Report(int x, int y) => System.Console.WriteLine($"{x} {y}"); + + static class E + { + public static ref readonly int M(this in int x) => ref x; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_Receiver() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + class C + { + static S M1() => new S { F = 111 }; + + static void M2(ref int x, ref int y) => System.Console.WriteLine($"{x} {y}"); + + static void Main() + { + M2(ref M1().Ref(), ref new S { F = 222 }.Ref()); + } + } + + struct S + { + public int F; + + [UnscopedRef] public ref int Ref() => ref F; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_Receiver_ReadOnlyContext_01() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + new S { F = 111 }.M3(); + + struct S + { + public int F; + + [UnscopedRef] public ref int Ref() => ref F; + + public readonly void M3() + { + M2(ref Ref(), ref new S { F = 222 }.Ref()); + } + + static void M2(ref int x, ref int y) => System.Console.WriteLine($"{x} {y}"); + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics( + // (13,16): warning CS8656: Call to non-readonly member 'S.Ref()' from a 'readonly' member results in an implicit copy of 'this'. + // M2(ref Ref(), ref new S { F = 222 }.Ref()); + Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "Ref").WithArguments("S.Ref()", "this").WithLocation(13, 16)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_CannotEscape_Receiver_ReadOnlyContext_02() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + static S M1() => new S { F = 111 }; + + M1().M3(); + + struct S + { + public int F; + + [UnscopedRef] public readonly ref readonly int Ref() => ref F; + + public readonly void M3() + { + M2(in Ref(), new S { F = 222 }); + } + + static void M2(in int x, S y) => System.Console.WriteLine($"{x} {y.F}"); + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics() + // One S temp is enough. + .VerifyIL("S.M3", """ + { + // Code size 33 (0x21) + .maxstack 3 + .locals init (S V_0) + IL_0000: ldarg.0 + IL_0001: call "readonly ref readonly int S.Ref()" + IL_0006: ldloca.s V_0 + IL_0008: initobj "S" + IL_000e: ldloca.s V_0 + IL_0010: ldc.i4 0xde + IL_0015: stfld "int S.F" + IL_001a: ldloc.0 + IL_001b: call "void S.M2(in int, S)" + IL_0020: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67435")] + public void RefTemp_Escapes_Receiver_ReadOnlyContext_03() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + class C + { + static S M1() => new S { F = 111 }; + + static void M2(in int x, in int y) => System.Console.WriteLine($"{x} {y}"); + + static void Main() + { + M2(in M1().Ref(), in new S { F = 222 }.Ref()); + } + } + + public struct S + { + public int F; + + [UnscopedRef] public readonly ref readonly int Ref() => ref F; + } + """; + CompileAndVerify(source, + expectedOutput: RefFieldTests.IncludeExpectedOutput("111 222"), + targetFramework: TargetFramework.Net70, + verify: Verification.Fails) + .VerifyDiagnostics(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72873")] public void Utf8Addition() { diff --git a/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs b/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs index 80e5bb29a3e59..c883dca5b9869 100644 --- a/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs +++ b/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs @@ -486,6 +486,9 @@ internal void EmitLocalStore(LocalDefinition local) internal void EmitLocalAddress(LocalDefinition local) { + Debug.Assert(LocalSlotManager != null); + LocalSlotManager.AddAddressedLocal(local, _optimizations); + if (local.IsReference) { EmitLocalLoad(local); diff --git a/src/Compilers/Core/Portable/CodeGen/LocalSlotManager.cs b/src/Compilers/Core/Portable/CodeGen/LocalSlotManager.cs index 6ab3b15df4910..991e7de5e3f20 100644 --- a/src/Compilers/Core/Portable/CodeGen/LocalSlotManager.cs +++ b/src/Compilers/Core/Portable/CodeGen/LocalSlotManager.cs @@ -66,6 +66,13 @@ public override bool Equals(object? obj) // pool of free slots partitioned by their signature. private KeyedStack? _freeSlots; + // these locals cannot be added to "FreeSlots" + private HashSet? _nonReusableLocals; + + // locals whose address has been taken; excludes non-reusable local kinds + private ArrayBuilder? _addressedLocals; + private int _addressedLocalScopes; + // all locals in order private ArrayBuilder? _lazyAllLocals; @@ -154,7 +161,8 @@ internal LocalDefinition GetLocal(ILocalSymbolInternal symbol) internal void FreeLocal(ILocalSymbolInternal symbol) { var slot = GetLocal(symbol); - LocalMap.Remove(symbol); + var removed = LocalMap.Remove(symbol); + Debug.Assert(removed, $"Attempted to free '{symbol}' more than once."); FreeSlot(slot); } @@ -245,7 +253,55 @@ private LocalDefinition DeclareLocalImpl( internal void FreeSlot(LocalDefinition slot) { Debug.Assert(slot.Name == null); - FreeSlots.Push(new LocalSignature(slot.Type, slot.Constraints), slot); + + if (_nonReusableLocals?.Remove(slot) != true) + { + FreeSlots.Push(new LocalSignature(slot.Type, slot.Constraints), slot); + } + } + + internal int StartScopeOfTrackingAddressedLocals() + { + Debug.Assert((_addressedLocals == null) == (_addressedLocalScopes == 0)); + _addressedLocals ??= ArrayBuilder.GetInstance(); + _addressedLocalScopes++; + return _addressedLocals.Count; + } + + internal void AddAddressedLocal(LocalDefinition localDef, OptimizationLevel optimizations) + { + // No need to add non-reusable local kinds to `_addressedLocals` because that list + // only contains locals with reusable kinds to mark them as actually non-reusable. + if (localDef != null && localDef.SymbolOpt?.SynthesizedKind.IsSlotReusable(optimizations) != false) + { + _addressedLocals?.Add(localDef); + } + } + + internal void EndScopeOfTrackingAddressedLocals(int countBefore, bool markAsNotReusable) + { + Debug.Assert(_addressedLocals != null); + + if (markAsNotReusable && countBefore < _addressedLocals.Count) + { + _nonReusableLocals ??= new HashSet(ReferenceEqualityComparer.Instance); + for (var i = countBefore; i < _addressedLocals.Count; i++) + { + _nonReusableLocals.Add(_addressedLocals[i]); + } + } + + _addressedLocalScopes--; + if (_addressedLocalScopes > 0) + { + _addressedLocals.Count = countBefore; + } + else + { + Debug.Assert(_addressedLocalScopes == 0 && countBefore == 0); + _addressedLocals.Free(); + _addressedLocals = null; + } } public ImmutableArray LocalsInOrder()