From b079ba096a46ca97def17f943e675b00248e43ca Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 16 Jul 2025 19:49:06 -0700 Subject: [PATCH 1/9] Temporarily add limited support for extension indexers --- .../Portable/Binder/Binder_Expressions.cs | 49 +++++++++++++ .../Portable/Binder/RefSafetyAnalysis.cs | 2 +- .../Portable/FlowAnalysis/NullableWalker.cs | 71 +++++++++++++++---- .../Source/SourceMemberContainerSymbol.cs | 4 +- 4 files changed, 109 insertions(+), 17 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index a2c295df9befa..408a67f1d7cc2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -10002,6 +10002,35 @@ private BoundExpression BindIndexerAccess(SyntaxNode node, BoundExpression expr, } else { + foreach (var scope in new ExtensionScopes(this)) + { + lookupResult.Clear(); + + scope.Binder.LookupAllExtensionMembersInSingleBinder( + lookupResult, WellKnownMemberNames.Indexer, arity: 0, lookupOptions, + originalBinder: this, useSiteInfo: ref useSiteInfo, classicExtensionUseSiteInfo: ref useSiteInfo); + + if (lookupResult.IsMultiViable) + { + ArrayBuilder indexerGroup = ArrayBuilder.GetInstance(); + foreach (Symbol symbol in lookupResult.Symbols) + { + Debug.Assert(symbol.IsIndexer()); + indexerGroup.Add((PropertySymbol)symbol); + } + + var actualMethodArguments = AnalyzedArguments.GetInstance(); + CombineExtensionMethodArguments(expr, analyzedArguments, actualMethodArguments); + + indexerAccessExpression = BindIndexerOrIndexedPropertyAccess(node, expr, indexerGroup, actualMethodArguments, diagnostics); + indexerGroup.Free(); + + actualMethodArguments.Free(); + lookupResult.Free(); + return indexerAccessExpression; + } + } + indexerAccessExpression = BadIndexerExpression(node, expr, analyzedArguments, lookupResult.Error, diagnostics); } } @@ -10242,6 +10271,26 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( // Make sure that the result of overload resolution is valid. var gotError = MemberGroupFinalValidationAccessibilityChecks(receiver, property, syntax, diagnostics, invokedAsExtensionMethod: false); + if (property.GetIsNewExtensionMember()) + { + // For new extension methods, we performed overload resolution giving the receiver as one of the arguments. + // We now restore the arguments to their original state and update the result accordingly. + analyzedArguments.Arguments.RemoveAt(0); + + if (analyzedArguments.Names is { Count: > 0 }) + { + analyzedArguments.Names.RemoveAt(0); + } + + if (analyzedArguments.RefKinds is { Count: > 0 }) + { + analyzedArguments.RefKinds.RemoveAt(0); + } + + Debug.Assert(resolutionResult.Result.ConversionForArg(0).Exists); + resolutionResult = resolutionResult.WithResult(resolutionResult.Result.WithoutReceiverArgument()); + } + receiver = ReplaceTypeOrValueReceiver(receiver, property.IsStatic, diagnostics); ImmutableArray argsToParams; diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index 00f9037787a3e..85ce032683691 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -283,7 +283,7 @@ private void TrackVisit(BoundNode? node) if (_visited is { } && _visited.Count <= MaxTrackVisited) { bool added = _visited.Add(expr); - RoslynDebug.Assert(added, $"Expression {expr} `{expr.Syntax}` visited more than once."); + //RoslynDebug.Assert(added, $"Expression {expr} `{expr.Syntax}` visited more than once."); } } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 2a470a46ea216..32595a3f0d46f 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -11378,25 +11378,68 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexerAccess(BoundIndexerAccess node) { - var receiverOpt = node.ReceiverOpt; - var receiverType = VisitRvalueWithState(receiverOpt).Type; - // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null - // after indices have been visited, and only if the receiver has not changed. - // Tracked by https://github.com/dotnet/roslyn/issues/78829: add support for indexers - _ = CheckPossibleNullReceiver(receiverOpt); - var indexer = node.Indexer; - if (receiverType is object) + + if (indexer.GetIsNewExtensionMember()) { - // Update indexer based on inferred receiver type. - indexer = (PropertySymbol)AsMemberOfType(receiverType, indexer); + Debug.Assert(node.ReceiverOpt is not null); + ImmutableArray arguments = [node.ReceiverOpt, ..node.Arguments]; + + var extensionParameter = indexer.ContainingType.ExtensionParameter; + Debug.Assert(extensionParameter is not null); + ImmutableArray parameters = [extensionParameter, ..indexer.Parameters]; + RefKind receiverRefKind = extensionParameter.RefKind == RefKind.Ref ? RefKind.Ref : RefKind.None; + var argumentRefKindsOpt = node.ArgumentRefKindsOpt; + + if (argumentRefKindsOpt.IsDefault) + { + if (receiverRefKind != RefKind.None) + { + var builder = ArrayBuilder.GetInstance(node.Arguments.Length + 1, fillWithValue: RefKind.None); + builder[0] = receiverRefKind; + argumentRefKindsOpt = builder.ToImmutableAndFree(); + } + } + else + { + argumentRefKindsOpt = [receiverRefKind, .. argumentRefKindsOpt]; + } + + // Tracked by https://github.com/dotnet/roslyn/issues/37238 : properties/indexers should account for NotNullIfNotNull + bool returnNotNull; + (var updatedProperty, _, returnNotNull) = VisitArguments(node, arguments, argumentRefKindsOpt, parameters, default, defaultArguments: default, + expanded: false, invokedAsExtensionMethod: false, indexer, firstArgumentResult: null); + + Debug.Assert(updatedProperty is not null); + + TypeWithAnnotations typeWithAnnotations = GetTypeOrReturnTypeWithAnnotations(updatedProperty); + FlowAnalysisAnnotations memberAnnotations = GetRValueAnnotations(updatedProperty); + TypeWithState typeWithState = ApplyUnconditionalAnnotations(typeWithAnnotations.ToTypeWithState(), memberAnnotations); + + SetResult(node, typeWithState, typeWithAnnotations); + SetUpdatedSymbol(node, node.Indexer, updatedProperty); } + else + { + var receiverOpt = node.ReceiverOpt; + var receiverType = VisitRvalueWithState(receiverOpt).Type; + // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null + // after indices have been visited, and only if the receiver has not changed. + // Tracked by https://github.com/dotnet/roslyn/issues/78829: add support for indexers + _ = CheckPossibleNullReceiver(receiverOpt); - VisitArguments(node, node.Arguments, node.ArgumentRefKindsOpt, indexer, node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded); + if (receiverType is object) + { + // Update indexer based on inferred receiver type. + indexer = (PropertySymbol)AsMemberOfType(receiverType, indexer); + } - var resultType = ApplyUnconditionalAnnotations(indexer.TypeWithAnnotations.ToTypeWithState(), GetRValueAnnotations(indexer)); - SetResult(node, resultType, indexer.TypeWithAnnotations); - SetUpdatedSymbol(node, node.Indexer, indexer); + VisitArguments(node, node.Arguments, node.ArgumentRefKindsOpt, indexer, node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded); + + var resultType = ApplyUnconditionalAnnotations(indexer.TypeWithAnnotations.ToTypeWithState(), GetRValueAnnotations(indexer)); + SetResult(node, resultType, indexer.TypeWithAnnotations); + SetUpdatedSymbol(node, node.Indexer, indexer); + } return null; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index fb9b111e317e1..b3a2b24bf2fa7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -4682,11 +4682,11 @@ internal static bool IsAllowedExtensionMember(Symbol member) break; case SymbolKind.Property: - if (!((PropertySymbol)member).IsIndexer) + //if (!((PropertySymbol)member).IsIndexer) { return true; } - break; + //break; case SymbolKind.Field: case SymbolKind.Event: From cf4369fa66baeb4071e473053cacfcfa0c0407f5 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 17 Jul 2025 03:29:15 -0700 Subject: [PATCH 2/9] Add tests for interesting scenarios --- .../LocalRewriter/LocalRewriter_Call.cs | 12 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 16 +- .../Test/Emit3/Semantics/ExtensionTests.cs | 817 ++- .../Test/Emit3/Semantics/ExtensionTests2.cs | 5337 +++++++++++++++++ 4 files changed, 6157 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 54bf270ca36ab..eea7d4bdaa927 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -660,6 +660,7 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded var requiresInstanceReceiver = methodOrIndexer.RequiresInstanceReceiver() && methodOrIndexer is not MethodSymbol { MethodKind: MethodKind.Constructor } and not FunctionPointerMethodSymbol; Debug.Assert(!requiresInstanceReceiver || rewrittenReceiver != null || _inExpressionLambda); Debug.Assert(!forceReceiverCapturing || (requiresInstanceReceiver && rewrittenReceiver != null && storesOpt is object)); + Debug.Assert(!forceReceiverCapturing || methodOrIndexer is PropertySymbol); BoundLocal? receiverTemp = null; BoundAssignmentOperator? assignmentToTemp = null; @@ -683,11 +684,14 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, // SPEC VIOLATION: as value types. - refKind = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter ? RefKind.Ref : RefKind.None; + refKind = (methodOrIndexer.GetIsNewExtensionMember() ? + !rewrittenReceiver.Type.IsReferenceType : + rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter) ? + RefKind.Ref : RefKind.None; } else { - if (rewrittenReceiver.Type.IsReferenceType) + if (rewrittenReceiver.Type.IsReferenceType) // IsNewExtensionMemberAccessWithByValPossiblyStructReceiver { refKind = RefKind.None; } @@ -778,7 +782,9 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded BoundAssignmentOperator? extraRefInitialization = null; if (receiverTemp.LocalSymbol.IsRef && - CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiverTemp) && + (methodOrIndexer.GetIsNewExtensionMember() ? + !receiverTemp.Type.IsValueType : + CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiverTemp)) && !CodeGenerator.ReceiverIsKnownToReferToTempIfReferenceType(receiverTemp) && (forceReceiverCapturing || !CodeGenerator.IsSafeToDereferenceReceiverRefAfterEvaluatingArguments(rewrittenArguments))) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 9c10562ecdfad..7925bc407f113 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -242,8 +242,20 @@ BoundExpression rewriteAssignment(BoundExpression leftRead, BoundExpression righ private static bool IsNewExtensionMemberAccessWithByValPossiblyStructReceiver(BoundExpression transformedLHS) { - return transformedLHS is BoundPropertyAccess { PropertySymbol: { } property } && property.GetIsNewExtensionMember() && - property.ContainingType.ExtensionParameter is { RefKind: RefKind.None, Type.IsReferenceType: false }; + switch (transformedLHS) + { + case BoundPropertyAccess { PropertySymbol: { } property }: + return checkProperty(property); + case BoundIndexerAccess { Indexer: { } indexer }: + return checkProperty(indexer); + default: + return false; + } + + static bool checkProperty(PropertySymbol property) + { + return property.GetIsNewExtensionMember() && !property.IsStatic && property.ContainingType.ExtensionParameter is { RefKind: RefKind.None, Type.IsReferenceType: false }; + } } private BoundExpression? TransformPropertyOrEventReceiver(Symbol propertyOrEvent, BoundExpression? receiverOpt, ArrayBuilder stores, ArrayBuilder temps) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index 2628ca1f35626..642073dc6039d 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -21080,20 +21080,70 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument """; var exeSource = """ - int i = 1; - i.M($""); - System.Console.Write(i); - i = 4; - E.M(ref i, $""); - System.Console.Write(i); + class Program + { + static void Main() + { + int i = 1; + Test1(ref i); + System.Console.Write(i); + i = 4; + Test2(ref i); + System.Console.Write(i); + } + + static void Test1(ref int i) + { + i.M($""); + } + + static void Test2(ref int i) + { + E.M(ref i, $""); + } + } """; - var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "123423" : null; - CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + var expectedOutput = "123423"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput).VerifyDiagnostics(); - var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net90); + verifier.VerifyIL("Program.Test1", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (int& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref int)" + IL_000b: call "void E.M(ref int, InterpolationHandler)" + IL_0010: ret + } + """); - CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) + verifier.VerifyIL("Program.Test2", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (int& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref int)" + IL_000b: call "void E.M(ref int, InterpolationHandler)" + IL_0010: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) .VerifyDiagnostics(); } @@ -21144,14 +21194,113 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] [CombinatorialData] - public void InterpolationHandler_ReceiverParameter_ByIn_WithConstantReceiver(bool useMetadataRef) + public void InterpolationHandler_ReceiverParameter_Generic_ByRef(bool useMetadataRef) { var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, ref TR i) + { + System.Console.Write(i); + i = (TR)(object)2; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public static class E + { + extension(ref T i) where T : struct + { + public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h) + { + System.Console.Write(i); + i = (T)(object)3; + } + } + } + """; + + var exeSource = """ + class Program + { + static void Main() + { + int i = 1; + Test1(ref i); + System.Console.Write(i); + i = 4; + Test2(ref i); + System.Console.Write(i); + } + + static void Test1(ref T i) where T : struct + { + i.M($""); + } + + static void Test2(ref T i) where T : struct + { + E.M(ref i, $""); + } + } + """; + + var expectedOutput = "123423"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (T& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref T)" + IL_000b: call "void E.M(ref T, InterpolationHandler)" + IL_0010: ret + } + """); + + verifier.VerifyIL("Program.Test2", """ + { + // Code size 17 (0x11) + .maxstack 4 + .locals init (T& V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj "InterpolationHandler..ctor(int, int, ref T)" + IL_000b: call "void E.M(ref T, InterpolationHandler)" + IL_0010: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [CombinatorialData] + public void InterpolationHandler_ReceiverParameter_ByIn_WithConstantReceiver(bool useMetadataRef, [CombinatorialValues("ref readonly", "in")] string refkind) + { + var src = $$$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, in int i) + public InterpolationHandler(int literalLength, int formattedCount, {{{refkind}}} int i) { System.Console.Write(i); System.Runtime.CompilerServices.Unsafe.AsRef(in i)++; @@ -21162,7 +21311,7 @@ public void AppendLiteral(string value) { } public static class E { - extension(in int i) + extension({{{refkind}}} int i) { public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h) { @@ -21173,12 +21322,64 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument """; var exeSource = """ - 1.M($""); - E.M(3, $""); + #pragma warning disable CS9193 // Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + + class Program + { + static void Main() + { + Test1(); + Test2(); + } + + static void Test1() + { + 1.M($""); + } + + static void Test2() + { + E.M(3, $""); + } + } """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "1234" : null; - CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", $$$""" + { + // Code size 19 (0x13) + .maxstack 4 + .locals init (int V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldloca.s V_0 + IL_0008: newobj "InterpolationHandler..ctor(int, int, {{{refkind}}} int)" + IL_000d: call "void E.M({{{refkind}}} int, InterpolationHandler)" + IL_0012: ret + } + """); + + verifier.VerifyIL("Program.Test2", $$$""" + { + // Code size 19 (0x13) + .maxstack 4 + .locals init (int V_0) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldloca.s V_0 + IL_0008: newobj "InterpolationHandler..ctor(int, int, {{{refkind}}} int)" + IL_000d: call "void E.M({{{refkind}}} int, InterpolationHandler)" + IL_0012: ret + } + """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net90); @@ -21530,8 +21731,8 @@ public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerA """; // Should be 0033, https://github.com/dotnet/roslyn/issues/79379 - var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "1033" : null; - var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) + var expectedOutput = "1033"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("", """ @@ -21564,9 +21765,585 @@ .locals init (MyStruct& V_0, } """); - var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net90); + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); - CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net90, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void InterpolationHandler_StructReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public struct MyStruct + { + public int i; + } + + public static class E + { + extension(T s) + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + Test1(); + Test2(); + Test3(); + Test4(); + } + + static void Test1() + { + E.field.M(Increment(), $""); + } + + static void Test2() + { + E.M(E.field, Increment(), $""); + } + + static void Test3() where T : struct + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : struct + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => E.field.i++; + } + """; + + var expectedOutput = "10337699"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + verifier.VerifyIL("Porgram.Test1", """ + { + // Code size 65 (0x41) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: ldsflda "T E.field" + IL_0005: stloc.2 + IL_0006: ldloca.s V_3 + IL_0008: initobj "T" + IL_000e: ldloc.3 + IL_000f: box "T" + IL_0014: brtrue.s IL_0021 + IL_0016: ldloc.2 + IL_0017: ldobj "T" + IL_001c: stloc.1 + IL_001d: ldloca.s V_1 + IL_001f: br.s IL_0022 + IL_0021: ldloc.2 + IL_0022: stloc.0 + IL_0023: ldloc.0 + IL_0024: ldobj "T" + IL_0029: call "int Porgram.Increment()" + IL_002e: ldc.i4.0 + IL_002f: ldc.i4.0 + IL_0030: ldloc.0 + IL_0031: ldobj "T" + IL_0036: newobj "InterpolationHandler..ctor(int, int, T)" + IL_003b: call "void E.M(T, int, InterpolationHandler)" + IL_0040: ret + } + """); + + verifier.VerifyIL("Porgram.Test2", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + verifier.VerifyIL("Porgram.Test3", """ + { + // Code size 36 (0x24) + .maxstack 5 + .locals init (T& V_0) + IL_0000: ldsflda "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldobj "T" + IL_000c: call "int Porgram.Increment()" + IL_0011: ldc.i4.0 + IL_0012: ldc.i4.0 + IL_0013: ldloc.0 + IL_0014: ldobj "T" + IL_0019: newobj "InterpolationHandler..ctor(int, int, T)" + IL_001e: call "void E.M(T, int, InterpolationHandler)" + IL_0023: ret + } + """); + + verifier.VerifyIL("Porgram.Test4", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void InterpolationHandler_StructReceiverParameter_GenericStruct_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public struct MyStruct + { + public int i; + } + + public static class E + { + extension(T s) where T : struct + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyStruct)(object)s).i); + E.field.i++; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + Test3(); + Test4(); + } + + static void Test3() where T : struct + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : struct + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => E.field.i++; + } + """; + + var expectedOutput = "1033"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + verifier.VerifyIL("Porgram.Test3", """ + { + // Code size 36 (0x24) + .maxstack 5 + .locals init (T& V_0) + IL_0000: ldsflda "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldobj "T" + IL_000c: call "int Porgram.Increment()" + IL_0011: ldc.i4.0 + IL_0012: ldc.i4.0 + IL_0013: ldloc.0 + IL_0014: ldobj "T" + IL_0019: newobj "InterpolationHandler..ctor(int, int, T)" + IL_001e: call "void E.M(T, int, InterpolationHandler)" + IL_0023: ret + } + """); + + verifier.VerifyIL("Porgram.Test4", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void InterpolationHandler_ClassReceiverParameter_GenericClass_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public class MyClass + { + public int i; + } + + public static class E + { + extension(T s) + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + E.field = new MyClass(); + Test1(); + Test2(); + Test3(); + Test4(); + } + + static void Test1() + { + E.field.M(Increment(), $""); + } + + static void Test2() + { + E.M(E.field, Increment(), $""); + } + + static void Test3() where T : class + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : class + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => (E.field = new MyClass() { i = E.field.i + 1 }).i; + } + """; + + var expectedOutput = "00336699"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + verifier.VerifyIL("Porgram.Test1", """ + { + // Code size 65 (0x41) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: ldsflda "T E.field" + IL_0005: stloc.2 + IL_0006: ldloca.s V_3 + IL_0008: initobj "T" + IL_000e: ldloc.3 + IL_000f: box "T" + IL_0014: brtrue.s IL_0021 + IL_0016: ldloc.2 + IL_0017: ldobj "T" + IL_001c: stloc.1 + IL_001d: ldloca.s V_1 + IL_001f: br.s IL_0022 + IL_0021: ldloc.2 + IL_0022: stloc.0 + IL_0023: ldloc.0 + IL_0024: ldobj "T" + IL_0029: call "int Porgram.Increment()" + IL_002e: ldc.i4.0 + IL_002f: ldc.i4.0 + IL_0030: ldloc.0 + IL_0031: ldobj "T" + IL_0036: newobj "InterpolationHandler..ctor(int, int, T)" + IL_003b: call "void E.M(T, int, InterpolationHandler)" + IL_0040: ret + } + """); + + verifier.VerifyIL("Porgram.Test2", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + verifier.VerifyIL("Porgram.Test3", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + verifier.VerifyIL("Porgram.Test4", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void InterpolationHandler_ClassReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) + { + var src = """ + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public struct InterpolationHandler + { + + public InterpolationHandler(int literalLength, int formattedCount, TR s) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } + + public class MyClass + { + public int i; + } + + public static class E + { + extension(T s) where T : class + { + public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h) + { + System.Console.Write(((MyClass)(object)s).i); + E.field = new MyClass() { i = E.field.i + 1 }; + } + } + } + + public static class E + { + public static T field; + } + """; + + var exeSource = """ + class Porgram + { + static void Main() + { + E.field = new MyClass(); + Test3(); + Test4(); + } + + static void Test3() where T : class + { + E.field.M(Increment(), $""); + } + + static void Test4() where T : class + { + E.M(E.field, Increment(), $""); + } + + static int Increment() => (E.field = new MyClass() { i = E.field.i + 1 }).i; + } + """; + + var expectedOutput = "0033"; + var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) + .VerifyDiagnostics(); + + verifier.VerifyIL("Porgram.Test3", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + verifier.VerifyIL("Porgram.Test4", """ + { + // Code size 26 (0x1a) + .maxstack 5 + .locals init (T V_0) + IL_0000: ldsfld "T E.field" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: call "int Porgram.Increment()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" + IL_0014: call "void E.M(T, int, InterpolationHandler)" + IL_0019: ret + } + """); + + var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], expectedOutput: expectedOutput) .VerifyDiagnostics(); } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 38ae48a5f4363..171cf1859bf11 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -10075,6 +10075,5343 @@ .locals init (int V_0) "); } + [Fact] + public void IndexerAccess_CompoundAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[0] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[0] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 39 (0x27) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: ldobj ""S1"" + IL_000c: ldc.i4.0 + IL_000d: call ""int E.get_Item(S1, int)"" + IL_0012: call ""int Program.Get1()"" + IL_0017: add + IL_0018: stloc.0 + IL_0019: ldobj ""S1"" + IL_001e: ldc.i4.0 + IL_001f: ldloc.0 + IL_0020: call ""void E.set_Item(S1, int, int)"" + IL_0025: nop + IL_0026: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: ldc.i4.0 + IL_0008: call ""int E.get_Item(S1, int)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldarg.0 + IL_0015: ldobj ""S1"" + IL_001a: ldc.i4.0 + IL_001b: ldloc.0 + IL_001c: call ""void E.set_Item(S1, int, int)"" + IL_0021: nop + IL_0022: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_CompoundAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[0] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[0] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: ldc.i4.0 + IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 23 (0x17) + .maxstack 4 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldc.i4.0 + IL_0003: ldarg.0 + IL_0004: ldc.i4.0 + IL_0005: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_000a: call ""int Program.Get1()"" + IL_000f: add + IL_0010: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0015: nop + IL_0016: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[0] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: ldc.i4.0 + IL_000b: call ""int E.get_Item(C1, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(C1, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[0] += Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 64 (0x40) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: ldc.i4.0 + IL_0026: call ""int E.get_Item(T, int)"" + IL_002b: call ""int Program.Get1()"" + IL_0030: add + IL_0031: stloc.3 + IL_0032: ldobj ""T"" + IL_0037: ldc.i4.0 + IL_0038: ldloc.3 + IL_0039: call ""void E.set_Item(T, int, int)"" + IL_003e: nop + IL_003f: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: ldc.i4.0 + IL_0009: call ""int E.get_Item(T, int)"" + IL_000e: call ""int Program.Get1()"" + IL_0013: add + IL_0014: stloc.0 + IL_0015: ldobj ""T"" + IL_001a: ldc.i4.0 + IL_001b: ldloc.0 + IL_001c: call ""void E.set_Item(T, int, int)"" + IL_0021: nop + IL_0022: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 25 (0x19) + .maxstack 4 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: ldc.i4.0 + IL_0007: call ""int E.get_Item(ref T, int)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_Item(ref T, int, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[0] += Get1(); + } + + static void Test2(ref T f) where T : class + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 64 (0x40) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: ldc.i4.0 + IL_0026: call ""int E.get_Item(T, int)"" + IL_002b: call ""int Program.Get1()"" + IL_0030: add + IL_0031: stloc.3 + IL_0032: ldobj ""T"" + IL_0037: ldc.i4.0 + IL_0038: ldloc.3 + IL_0039: call ""void E.set_Item(T, int, int)"" + IL_003e: nop + IL_003f: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 30 (0x1e) + .maxstack 4 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.0 + IL_000a: ldloc.0 + IL_000b: ldc.i4.0 + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: add + IL_0017: call ""void E.set_Item(T, int, int)"" + IL_001c: nop + IL_001d: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_07() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[0] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[0] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 73 (0x49) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + T V_3, + int V_4) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.2 + IL_0007: ldloca.s V_2 + IL_0009: stloc.1 + IL_000a: ldloca.s V_3 + IL_000c: initobj ""T"" + IL_0012: ldloc.3 + IL_0013: box ""T"" + IL_0018: brtrue.s IL_0025 + IL_001a: ldloc.1 + IL_001b: ldobj ""T"" + IL_0020: stloc.0 + IL_0021: ldloca.s V_0 + IL_0023: br.s IL_0026 + IL_0025: ldloc.1 + IL_0026: dup + IL_0027: ldobj ""T"" + IL_002c: ldc.i4.0 + IL_002d: call ""int E.get_Item(T, int)"" + IL_0032: call ""int Program.Get1()"" + IL_0037: add + IL_0038: stloc.s V_4 + IL_003a: ldobj ""T"" + IL_003f: ldc.i4.0 + IL_0040: ldloc.s V_4 + IL_0042: call ""void E.set_Item(T, int, int)"" + IL_0047: nop + IL_0048: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_08() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[0] += Get1(); + } + + static void Test2() where T : class + { + GetT()[0] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[0] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 73 (0x49) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + T V_3, + int V_4) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.2 + IL_0007: ldloca.s V_2 + IL_0009: stloc.1 + IL_000a: ldloca.s V_3 + IL_000c: initobj ""T"" + IL_0012: ldloc.3 + IL_0013: box ""T"" + IL_0018: brtrue.s IL_0025 + IL_001a: ldloc.1 + IL_001b: ldobj ""T"" + IL_0020: stloc.0 + IL_0021: ldloca.s V_0 + IL_0023: br.s IL_0026 + IL_0025: ldloc.1 + IL_0026: dup + IL_0027: ldobj ""T"" + IL_002c: ldc.i4.0 + IL_002d: call ""int E.get_Item(T, int)"" + IL_0032: call ""int Program.Get1()"" + IL_0037: add + IL_0038: stloc.s V_4 + IL_003a: ldobj ""T"" + IL_003f: ldc.i4.0 + IL_0040: ldloc.s V_4 + IL_0042: call ""void E.set_Item(T, int, int)"" + IL_0047: nop + IL_0048: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: ldc.i4.0 + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(T, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 63 (0x3f) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: ldobj ""S1"" + IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001a: stloc.2 + IL_001b: ldloc.0 + IL_001c: ldobj ""S1"" + IL_0021: ldloc.1 + IL_0022: ldloc.2 + IL_0023: call ""int E.get_Item(S1, int, InterpolationHandler)"" + IL_0028: call ""int Program.Get1()"" + IL_002d: add + IL_002e: stloc.3 + IL_002f: ldloc.0 + IL_0030: ldobj ""S1"" + IL_0035: ldloc.1 + IL_0036: ldloc.2 + IL_0037: ldloc.3 + IL_0038: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_003d: nop + IL_003e: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 59 (0x3b) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""S1"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldobj ""S1"" + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int E.get_Item(S1, int, InterpolationHandler)"" + IL_0024: call ""int Program.Get1()"" + IL_0029: add + IL_002a: stloc.3 + IL_002b: ldloc.0 + IL_002c: ldobj ""S1"" + IL_0031: ldloc.1 + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_0039: nop + IL_003a: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] += 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 46 (0x2e) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 42 (0x2a) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: stloc.2 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloc.0 + IL_0016: ldloc.1 + IL_0017: ldloc.2 + IL_0018: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" + IL_001d: call ""int Program.Get1()"" + IL_0022: add + IL_0023: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_0028: nop + IL_0029: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] += 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 46 (0x2e) + .maxstack 6 + .locals init (C1 V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(C1, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret +} +"); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] += Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F[Get1(), $""] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127:124125127127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 94 (0x5e) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + int V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: ldloc.0 + IL_0037: ldobj ""T"" + IL_003c: ldloc.3 + IL_003d: ldloc.s V_4 + IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0044: call ""int Program.Get1()"" + IL_0049: add + IL_004a: stloc.s V_6 + IL_004c: ldloc.0 + IL_004d: ldobj ""T"" + IL_0052: ldloc.3 + IL_0053: ldloc.s V_4 + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_005c: nop + IL_005d: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 59 (0x3b) + .maxstack 4 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""T"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldobj ""T"" + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0024: call ""int Program.Get1()"" + IL_0029: add + IL_002a: stloc.3 + IL_002b: ldloc.0 + IL_002c: ldobj ""T"" + IL_0031: ldloc.1 + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0039: nop + IL_003a: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[Get1(), $""] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 42 (0x2a) + .maxstack 6 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: stloc.2 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloc.0 + IL_0016: ldloc.1 + IL_0017: ldloc.2 + IL_0018: call ""int E.get_Item(ref T, int, InterpolationHandler)"" + IL_001d: call ""int Program.Get1()"" + IL_0022: add + IL_0023: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" + IL_0028: nop + IL_0029: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] += Get1(); + } + + static void Test2(ref T f) where T : class + { + f[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + Program.F[Get1(), $""] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123127:123123123127:123123123127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 94 (0x5e) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + int V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: ldloc.0 + IL_0037: ldobj ""T"" + IL_003c: ldloc.3 + IL_003d: ldloc.s V_4 + IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0044: call ""int Program.Get1()"" + IL_0049: add + IL_004a: stloc.s V_6 + IL_004c: ldloc.0 + IL_004d: ldobj ""T"" + IL_0052: ldloc.3 + IL_0053: ldloc.s V_4 + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_005c: nop + IL_005d: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 47 (0x2f) + .maxstack 6 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: stloc.1 + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: ldloc.0 + IL_001b: ldloc.1 + IL_001c: ldloc.2 + IL_001d: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0022: call ""int Program.Get1()"" + IL_0027: add + IL_0028: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002d: nop + IL_002e: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_07() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[Get1(), $""] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 102 (0x66) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + T V_6, + int V_7) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.s V_5 + IL_0008: ldloca.s V_5 + IL_000a: stloc.2 + IL_000b: ldloca.s V_6 + IL_000d: initobj ""T"" + IL_0013: ldloc.s V_6 + IL_0015: box ""T"" + IL_001a: brtrue.s IL_0027 + IL_001c: ldloc.2 + IL_001d: ldobj ""T"" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: br.s IL_0028 + IL_0027: ldloc.2 + IL_0028: stloc.0 + IL_0029: call ""int Program.Get1()"" + IL_002e: stloc.3 + IL_002f: ldc.i4.0 + IL_0030: ldc.i4.0 + IL_0031: ldloc.0 + IL_0032: ldobj ""T"" + IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_003c: stloc.s V_4 + IL_003e: ldloc.0 + IL_003f: ldobj ""T"" + IL_0044: ldloc.3 + IL_0045: ldloc.s V_4 + IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_004c: call ""int Program.Get1()"" + IL_0051: add + IL_0052: stloc.s V_7 + IL_0054: ldloc.0 + IL_0055: ldobj ""T"" + IL_005a: ldloc.3 + IL_005b: ldloc.s V_4 + IL_005d: ldloc.s V_7 + IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0064: nop + IL_0065: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_08() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] += Get1(); + } + + static void Test2() where T : class + { + GetT()[Get1(), $""] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[Get1(), $""] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123:123123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 102 (0x66) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + T V_6, + int V_7) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.s V_5 + IL_0008: ldloca.s V_5 + IL_000a: stloc.2 + IL_000b: ldloca.s V_6 + IL_000d: initobj ""T"" + IL_0013: ldloc.s V_6 + IL_0015: box ""T"" + IL_001a: brtrue.s IL_0027 + IL_001c: ldloc.2 + IL_001d: ldobj ""T"" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: br.s IL_0028 + IL_0027: ldloc.2 + IL_0028: stloc.0 + IL_0029: call ""int Program.Get1()"" + IL_002e: stloc.3 + IL_002f: ldc.i4.0 + IL_0030: ldc.i4.0 + IL_0031: ldloc.0 + IL_0032: ldobj ""T"" + IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_003c: stloc.s V_4 + IL_003e: ldloc.0 + IL_003f: ldobj ""T"" + IL_0044: ldloc.3 + IL_0045: ldloc.s V_4 + IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_004c: call ""int Program.Get1()"" + IL_0051: add + IL_0052: stloc.s V_7 + IL_0054: ldloc.0 + IL_0055: ldobj ""T"" + IL_005a: ldloc.3 + IL_005b: ldloc.s V_4 + IL_005d: ldloc.s V_7 + IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0064: nop + IL_0065: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 46 (0x2e) + .maxstack 6 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret +} +"); + } + + [Fact] + public void IndexerAccess_Set_WithInterpolationHandler_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] = Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] = Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 43 (0x2b) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""int Program.Get1()"" + IL_0012: ldc.i4.0 + IL_0013: ldc.i4.0 + IL_0014: ldloc.0 + IL_0015: ldobj ""S1"" + IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001f: call ""int Program.Get1()"" + IL_0024: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_0029: nop + IL_002a: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 39 (0x27) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""S1"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""S1"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_0025: nop + IL_0026: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] = 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Set_WithInterpolationHandler_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] = Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] = Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] = 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); + } + + [Fact] + public void IndexerAccess_Set_WithInterpolationHandler_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_WithInterpolationHandler_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] = Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1(), $""] = await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 68 (0x44) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0042: nop + IL_0043: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 39 (0x27) + .maxstack 5 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""T"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0025: nop + IL_0026: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] = 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void IndexerAccess_Set_WithInterpolationHandler_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[Get1(), $""] = await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0, $""] = 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_WithInterpolationHandler_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] = Get1(); + } + + static void Test2(ref T f) where T : class + { + f[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1(), $""] = await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 68 (0x44) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0042: nop + IL_0043: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 34 (0x22) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0020: nop + IL_0021: ret +} +"); + } + + [Fact] + public void IndexerAccess_Set_WithInterpolationHandler_07() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] = Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[Get1(), $""] = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Fact] + public void IndexerAccess_Set_WithInterpolationHandler_08() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[Get1(), $""] = Get1(); + } + + static void Test2() where T : class + { + GetT()[Get1(), $""] = Get1(); + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT()[Get1(), $""] = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + _ = this[Program.Get1(), $"", Program.Get1()]; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Program.Get1(), $"", Get1()]; + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 43 (0x2b) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""int Program.Get1()"" + IL_0012: ldc.i4.0 + IL_0013: ldc.i4.0 + IL_0014: ldloc.0 + IL_0015: ldobj ""S1"" + IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001f: call ""int Program.Get1()"" + IL_0024: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_0029: pop + IL_002a: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 39 (0x27) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""S1"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""S1"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_0025: pop + IL_0026: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + _ = this[Program.Get1(), $"", Program.Get1()]; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Program.Get1(), $"", Get1()]; + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + _ = default(S1)[0, $"", 1]; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! + comp2.VerifyDiagnostics( + ); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + _ = F[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + _ = f[Get1(), $"", Get1()]; + } + + static void Test2(ref T f) where T : struct + { + _ = f[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[Get1(), $"", await Get1Async()]; + //} + + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 68 (0x44) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0042: pop + IL_0043: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 39 (0x27) + .maxstack 5 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""T"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0025: pop + IL_0026: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + _ = f[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + _ = Program.F[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + _ = default(T)[0, $"", 1]; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! + comp2.VerifyDiagnostics( + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + _ = f[Get1(), $"", Get1()]; + } + + static void Test2(ref T f) where T : class + { + _ = f[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[Get1(), $"", await Get1Async()]; + //} + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 68 (0x44) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0042: pop + IL_0043: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 34 (0x22) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +public struct S1 +{ + public int F1; +} + +class Program +{ + static void Main() + { + Test(); + } + + static void Test() + { + _ = GetS1()[Program.Get1(), $"", Get1()]; + } + + static S1 GetS1() => new S1 { F1 = 123 }; + + public static int Get1() + { + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1 V_0) + IL_0000: nop + IL_0001: call ""S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_02(string refKind) + { + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +unsafe struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + fixed (int* f1 = &x.F1) + { + (*f1)++; + } + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static void Main() + { + Test(); + } + + static void Test() + { + _ = GetS1()[Program.Get1(), $"", Get1()]; + } + + static S1 GetS1() => new S1 { F1 = 123 }; + + public static int Get1() + { + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "123124", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 35 (0x23) + .maxstack 5 + .locals init (S1 V_0) + IL_0000: nop + IL_0001: call ""S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloca.s V_0 + IL_0012: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0017: call ""int Program.Get1()"" + IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static void Main() + { + Test(); + } + + static void Test() + { + _ = GetC1()[Get1(), $"", Get1()]; + } + + static C1 GetC1() => new C1 { F1 = 123 }; + + static int Get1() + { + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: call ""C1 Program.GetC1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static void Test2() where T : struct + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + x = (TR)(object)new S1 { F1 = ((S1)(object)x).F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static void Test2() where T : struct + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static int Get1() + { + return 1; + } + + static async Task Test3() where T : struct + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 35 (0x23) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloca.s V_0 + IL_0012: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0017: call ""int Program.Get1()"" + IL_001c: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_06() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static void Test2() where T : class + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + [Fact] public void GroupingTypeRawName_01() { From df394464891ff739484ba49d23945d2c4b47045e Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 17 Jul 2025 09:54:44 -0700 Subject: [PATCH 3/9] More interesting tests --- .../Test/Emit3/Semantics/ExtensionTests2.cs | 12617 ++++++++++------ 1 file changed, 7856 insertions(+), 4761 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 171cf1859bf11..5b46be53c712a 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -5567,7 +5567,7 @@ static class E } [Fact] - public void PropertyAccess_CompoundAssignment_01() + public void PropertyAccess_Set_01() { var src = """ static class E @@ -5578,8 +5578,6 @@ public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; return 0; } set @@ -5590,13 +5588,13 @@ public int P1 } } -struct S1 +public struct S1 { public int F1; public void Test() { - this.P1 += Program.Get1(); + this.P1 = Program.Get1(); } } @@ -5619,7 +5617,7 @@ static void Main() static void Test() { - F.P1 += Get1(); + F.P1 = Get1(); } public static int Get1() @@ -5631,58 +5629,70 @@ public static int Get1() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 37 (0x25) - .maxstack 3 - .locals init (int V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: call ""int E.get_P1(S1)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: add - IL_0017: stloc.0 - IL_0018: ldobj ""S1"" - IL_001d: ldloc.0 - IL_001e: call ""void E.set_P1(S1, int)"" - IL_0023: nop - IL_0024: ret + IL_0001: ldsfld ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(S1, int)"" + IL_0010: nop + IL_0011: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 33 (0x21) + // Code size 19 (0x13) .maxstack 2 - .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj ""S1"" - IL_0007: call ""int E.get_P1(S1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: stloc.0 - IL_0013: ldarg.0 - IL_0014: ldobj ""S1"" - IL_0019: ldloc.0 - IL_001a: call ""void E.set_P1(S1, int)"" - IL_001f: nop - IL_0020: ret + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_P1(S1, int)"" + IL_0011: nop + IL_0012: ret } "); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1).P1 = 1; + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void PropertyAccess_CompoundAssignment_02(string refKind) + public void PropertyAccess_Set_02(string refKind) { var src = $$$""" static class E @@ -5711,7 +5721,7 @@ struct S1 public void Test() { - this.P1 += Program.Get1(); + this.P1 = Program.Get1(); } } @@ -5734,7 +5744,7 @@ static void Main() static void Test() { - F.P1 += Get1(); + F.P1 = Get1(); } public static int Get1() @@ -5745,40 +5755,34 @@ public static int Get1() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 25 (0x19) - .maxstack 3 + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: call ""int E.get_P1(" + refKind + @" S1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0017: nop - IL_0018: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0010: nop + IL_0011: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 21 (0x15) - .maxstack 3 + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldarg.0 - IL_0003: call ""int E.get_P1(" + refKind + @" S1)"" - IL_0008: call ""int Program.Get1()"" - IL_000d: add - IL_000e: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0013: nop - IL_0014: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_000c: nop + IL_000d: ret } "); @@ -5797,35 +5801,35 @@ class Program { static void Test() { - default(S1).P1 += 1; + default(S1).P1 = 1; } } """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2]); switch (refKind) { case "ref": comp2.VerifyDiagnostics( // (15,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) ); break; case "ref readonly": comp2.VerifyDiagnostics( // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) ); break; case "in": comp2.VerifyDiagnostics( // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 += 1; + // default(S1).P1 = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) ); break; @@ -5835,7 +5839,7 @@ static void Test() } [Fact] - public void PropertyAccess_CompoundAssignment_03() + public void PropertyAccess_Set_03() { var src = """ static class E @@ -5876,7 +5880,7 @@ static void Main() static void Test() { - F.P1 += Get1(); + F.P1 = Get1(); } static int Get1() @@ -5887,29 +5891,27 @@ static int Get1() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 25 (0x19) - .maxstack 3 + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: ldsfld ""C1 Program.F"" - IL_0006: dup - IL_0007: call ""int E.get_P1(C1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: call ""void E.set_P1(C1, int)"" - IL_0017: nop - IL_0018: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(C1, int)"" + IL_0010: nop + IL_0011: ret } "); } [Fact] - public void PropertyAccess_CompoundAssignment_04() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void PropertyAccess_Set_04() { var src = """ using System.Threading.Tasks; @@ -5946,6 +5948,8 @@ class Program class Program { +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -5958,21 +5962,22 @@ static async Task Main() Test2(ref Program.F); System.Console.Write(Program.F.F1); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f.P1 += Get1(); + f.P1 = Get1(); } static void Test2(ref T f) where T : struct { - f.P1 += Get1(); + f.P1 = Get1(); } static int Get1() @@ -5981,85 +5986,108 @@ static int Get1() return 1; } - static async Task Test3() - { - Program.F.P1 += await Get1Async(); - } + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F.P1 = await Get1Async(); + //} - static async Task Get1Async() - { - Program.F.F1++; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 62 (0x3e) - .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 - IL_0005: initobj ""T"" - IL_000b: ldloc.2 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""int E.get_P1(T)"" - IL_002a: call ""int Program.Get1()"" - IL_002f: add - IL_0030: stloc.3 - IL_0031: ldobj ""T"" - IL_0036: ldloc.3 - IL_0037: call ""void E.set_P1(T, int)"" - IL_003c: nop - IL_003d: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_P1(T, int)"" + IL_0011: nop + IL_0012: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 33 (0x21) - .maxstack 3 - .locals init (int V_0) + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: ldobj ""T"" - IL_0008: call ""int E.get_P1(T)"" - IL_000d: call ""int Program.Get1()"" - IL_0012: add - IL_0013: stloc.0 - IL_0014: ldobj ""T"" - IL_0019: ldloc.0 - IL_001a: call ""void E.set_P1(T, int)"" - IL_001f: nop - IL_0020: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_P1(T, int)"" + IL_0011: nop + IL_0012: ret } "); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int P1 { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T).P1 = 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation([src2]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T).P1 = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T).P1").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); } [Fact] - public void PropertyAccess_CompoundAssignment_05() + public void PropertyAccess_Set_05() { var src = """ using System.Threading.Tasks; @@ -6068,7 +6096,7 @@ static class E { extension(ref T x) where T : struct { - public int P1 + public int P1 { get { @@ -6111,7 +6139,7 @@ static async Task Main() static void Test2(ref T f) where T : struct { - f.P1 += Get1(); + f.P1 = Get1(); } static int Get1() @@ -6122,7 +6150,7 @@ static int Get1() static async Task Test3() where T : struct { - Program.F.P1 += await Get1Async(); + Program.F.P1 = await Get1Async(); } static async Task Get1Async() @@ -6134,23 +6162,20 @@ static async Task Get1Async() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 21 (0x15) - .maxstack 3 + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: call ""int E.get_P1(ref T)"" - IL_0008: call ""int Program.Get1()"" - IL_000d: add - IL_000e: call ""void E.set_P1(ref T, int)"" - IL_0013: nop - IL_0014: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""void E.set_P1(ref T, int)"" + IL_000c: nop + IL_000d: ret } "); @@ -6167,7 +6192,7 @@ class Program { static void Test() where T : struct { - default(T).P1 += 1; + default(T).P1 = 1; } } @@ -6192,10 +6217,10 @@ static class E } """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2]); comp2.VerifyDiagnostics( // (13,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P1 += 1; + // default(T).P1 = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension(in T x) where T : struct @@ -6207,7 +6232,8 @@ static class E } [Fact] - public void PropertyAccess_CompoundAssignment_06() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void PropertyAccess_Set_06() { var src = """ using System.Threading.Tasks; @@ -6244,6 +6270,8 @@ class Program class Program { +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new C1 { F1 = 123 }; @@ -6256,21 +6284,22 @@ static async Task Main() Test2(ref Program.F); System.Console.Write(Program.F.F1); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f.P1 += Get1(); + f.P1 = Get1(); } static void Test2(ref T f) where T : class { - f.P1 += Get1(); + f.P1 = Get1(); } static int Get1() @@ -6279,98 +6308,75 @@ static int Get1() return 1; } - static async Task Test3() - { - Program.F.P1 += await Get1Async(); - } + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F.P1 = await Get1Async(); + //} - static async Task Get1Async() - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 62 (0x3e) - .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 - IL_0005: initobj ""T"" - IL_000b: ldloc.2 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""int E.get_P1(T)"" - IL_002a: call ""int Program.Get1()"" - IL_002f: add - IL_0030: stloc.3 - IL_0031: ldobj ""T"" - IL_0036: ldloc.3 - IL_0037: call ""void E.set_P1(T, int)"" - IL_003c: nop - IL_003d: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_P1(T, int)"" + IL_0011: nop + IL_0012: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 26 (0x1a) - .maxstack 3 + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj ""T"" - IL_0007: dup - IL_0008: call ""int E.get_P1(T)"" - IL_000d: call ""int Program.Get1()"" - IL_0012: add - IL_0013: call ""void E.set_P1(T, int)"" - IL_0018: nop - IL_0019: ret + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_P1(T, int)"" + IL_0011: nop + IL_0012: ret } "); } [Fact] - public void PropertyAccess_PrefixIncrementAssignment_01() + public void PropertyAccess_Set_07() { var src = """ +using System.Threading.Tasks; + static class E { - extension(S1 x) + extension(T x) { - public S2 P1 + public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return default; + System.Console.Write(((S1)(object)x).F1); + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -6379,270 +6385,184 @@ public S2 P1 struct S1 { public int F1; - - public void Test() - { - ++this.P1; - } } -struct S2 +class Program { - public static S2 operator ++(S2 x) + static async Task Main() { - Program.F.F1++; - return x; + Test1(); + + System.Console.Write(":"); + + await Test3(); } -} -class Program -{ - public static S1 F; + static T GetT() => (T)(object)new S1 { F1 = 123 }; - static void Main() + static void Test1() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + GetT().P1 = Get1(); + } - System.Console.Write(":"); + static int Get1() + { + return 1; + } - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + static async Task Test3() + { + GetT().P1 = await Get1Async(); } - static void Test() + static async Task Get1Async() { - ++F.P1; + await Task.Yield(); + return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 36 (0x24) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: call ""S2 E.get_P1(S1)"" - IL_0011: call ""S2 S2.op_Increment(S2)"" - IL_0016: stloc.0 - IL_0017: ldobj ""S1"" - IL_001c: ldloc.0 - IL_001d: call ""void E.set_P1(S1, S2)"" - IL_0022: nop - IL_0023: ret -} -"); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123").VerifyDiagnostics(); - verifier.VerifyIL("S1.Test", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 32 (0x20) + // Code size 18 (0x12) .maxstack 2 - .locals init (S2 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""S2 E.get_P1(S1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldarg.0 - IL_0013: ldobj ""S1"" - IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(S1, S2)"" - IL_001e: nop - IL_001f: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(T, int)"" + IL_0010: nop + IL_0011: ret } "); } - [Theory] - [InlineData("ref")] - [InlineData("ref readonly")] - [InlineData("in")] - public void PropertyAccess_PrefixIncrementAssignment_02(string refKind) + [Fact] + public void PropertyAccess_Set_08() { - var src = $$$""" + var src = """ +using System.Threading.Tasks; + static class E { - extension({{{refKind}}} S1 x) + extension(T x) { - public S2 P1 + public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return default; + System.Console.Write(((C1)(object)x).F1); + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((C1)(object)x).F1); } } } } -struct S1 +class C1 { public int F1; - - public void Test() - { - ++this.P1; - } } -struct S2 +class Program { - public static S2 operator ++(S2 x) + static async Task Main() { - Program.F.F1++; - return x; - } -} + Test1(); -class Program -{ - public static S1 F; + System.Console.Write(":"); - static void Main() - { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + Test2(); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + await Test3(); } - static void Test() + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() { - ++F.P1; + GetT().P1 = Get1(); } -} -"""; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + static void Test2() where T : class + { + GetT().P1 = Get1(); + } - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 26 (0x1a) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldloc.0 - IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0018: nop - IL_0019: ret + static int Get1() + { + return 1; + } + + static async Task Test3() + { + GetT().P1 = await Get1Async(); + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } -"); +"""; - verifier.VerifyIL("S1.Test", + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", @" { - // Code size 22 (0x16) + // Code size 18 (0x12) .maxstack 2 - .locals init (S2 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_0007: call ""S2 S2.op_Increment(S2)"" - IL_000c: stloc.0 - IL_000d: ldarg.0 - IL_000e: ldloc.0 - IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0014: nop - IL_0015: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(T, int)"" + IL_0010: nop + IL_0011: ret } "); - var src2 = $$$""" -static class E -{ - extension({{{refKind}}} S1 x) - { - public int P1 { get => 0; set {} } - } -} - -struct S1; - -class Program + verifier.VerifyIL("Program.Test2()", +@" { - static void Test() - { - ++default(S1).P1; - } + // Code size 18 (0x12) + .maxstack 2 + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""void E.set_P1(T, int)"" + IL_0010: nop + IL_0011: ret } -"""; - - var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (15,11): error CS1510: A ref or out value must be an assignable variable - // ++default(S1).P1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 11) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (15,11): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // ++default(S1).P1; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 11), - // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // ++default(S1).P1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // ++default(S1).P1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } +"); } [Fact] - public void PropertyAccess_PrefixIncrementAssignment_03() + public void PropertyAccess_CompoundAssignment_01() { var src = """ static class E { - extension(C1 x) + extension(S1 x) { - public S2 P1 + public int P1 { get { System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return default; + Program.F.F1++; + return 0; } set { @@ -6652,79 +6572,116 @@ public S2 P1 } } -class C1 +struct S1 { public int F1; -} -struct S2 -{ - public static S2 operator ++(S2 x) + public void Test() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return x; + this.P1 += Program.Get1(); } } class Program { - public static C1 F = new C1 { F1 = 123 }; + public static S1 F; static void Main() { + F = new S1 { F1 = 123 }; Test(); System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); } static void Test() { - ++F.P1; + F.P1 += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 26 (0x1a) - .maxstack 2 - .locals init (S2 V_0) + // Code size 37 (0x25) + .maxstack 3 + .locals init (int V_0) IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" + IL_0001: ldsflda ""S1 Program.F"" IL_0006: dup - IL_0007: call ""S2 E.get_P1(C1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldloc.0 - IL_0013: call ""void E.set_P1(C1, S2)"" - IL_0018: nop - IL_0019: ret + IL_0007: ldobj ""S1"" + IL_000c: call ""int E.get_P1(S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: add + IL_0017: stloc.0 + IL_0018: ldobj ""S1"" + IL_001d: ldloc.0 + IL_001e: call ""void E.set_P1(S1, int)"" + IL_0023: nop + IL_0024: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""int E.get_P1(S1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: stloc.0 + IL_0013: ldarg.0 + IL_0014: ldobj ""S1"" + IL_0019: ldloc.0 + IL_001a: call ""void E.set_P1(S1, int)"" + IL_001f: nop + IL_0020: ret } "); } - [Fact] - public void PropertyAccess_PrefixIncrementAssignment_04() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_CompoundAssignment_02(string refKind) { - var src = """ + var src = $$$""" static class E { - extension(T x) + extension({{{refKind}}} S1 x) { - public S2 P1 + public int P1 { get { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); Program.F.F1++; - return default; + return 0; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); } } } @@ -6733,14 +6690,10 @@ public S2 P1 struct S1 { public int F1; -} -struct S2 -{ - public static S2 operator ++(S2 x) + public void Test() { - Program.F.F1++; - return x; + this.P1 += Program.Get1(); } } @@ -6751,24 +6704,25 @@ class Program static void Main() { F = new S1 { F1 = 123 }; - Test1(ref F); + Test(); System.Console.Write(F.F1); System.Console.Write(":"); F = new S1 { F1 = 123 }; - Test2(ref F); + F.Test(); System.Console.Write(F.F1); } - static void Test1(ref T f) + static void Test() { - ++f.P1; + F.P1 += Get1(); } - static void Test2(ref T f) where T : struct + public static int Get1() { - ++f.P1; + Program.F.F1++; + return 1; } } """; @@ -6776,279 +6730,271 @@ static void Test2(ref T f) where T : struct var comp = CreateCompilation(src, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 61 (0x3d) - .maxstack 2 - .locals init (T V_0, - T& V_1, - S2 V_2, - T V_3) + // Code size 25 (0x19) + .maxstack 3 + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: call ""int E.get_P1(" + refKind + @" S1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 21 (0x15) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret + IL_0002: ldarg.0 + IL_0003: call ""int E.get_P1(" + refKind + @" S1)"" + IL_0008: call ""int Program.Get1()"" + IL_000d: add + IL_000e: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0013: nop + IL_0014: ret } "); - verifier.VerifyIL("Program.Test2(ref T)", -@" + var src2 = $$$""" +static class E { - // Code size 32 (0x20) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: dup - IL_0003: ldobj ""T"" - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: call ""S2 S2.op_Increment(S2)"" - IL_0012: stloc.0 - IL_0013: ldobj ""T"" - IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(T, S2)"" - IL_001e: nop - IL_001f: ret + extension({{{refKind}}} S1 x) + { + public int P1 { get => 0; set {} } + } } -"); + +struct S1; + +class Program +{ + static void Test() + { + default(S1).P1 += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P1 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P1 += 1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } } [Fact] - public void PropertyAccess_PrefixIncrementAssignment_05() + public void PropertyAccess_CompoundAssignment_03() { var src = """ static class E { - extension(ref T x) where T : struct + extension(C1 x) { - public S2 P1 + public int P1 { get { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return default; + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); } } } } -struct S1 +class C1 { public int F1; } -struct S2 -{ - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; - } -} - class Program { - public static S1 F; + public static C1 F; static void Main() { - F = new S1 { F1 = 123 }; - Test2(ref F); + F = new C1 { F1 = 123 }; + Test(); System.Console.Write(F.F1); } - static void Test2(ref T f) where T : struct + static void Test() { - ++f.P1; + F.P1 += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 22 (0x16) - .maxstack 2 - .locals init (S2 V_0) + // Code size 25 (0x19) + .maxstack 3 IL_0000: nop - IL_0001: ldarg.0 - IL_0002: dup - IL_0003: call ""S2 E.get_P1(ref T)"" - IL_0008: call ""S2 S2.op_Increment(S2)"" - IL_000d: stloc.0 - IL_000e: ldloc.0 - IL_000f: call ""void E.set_P1(ref T, S2)"" - IL_0014: nop - IL_0015: ret + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: dup + IL_0007: call ""int E.get_P1(C1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_P1(C1, int)"" + IL_0017: nop + IL_0018: ret } "); - - var src2 = """ -static class E -{ - extension(ref T x) where T : struct - { - public int P1 { get => 0; set {} } - } -} - -class Program -{ - static void Test() where T : struct - { - ++default(T).P1; - } -} - -namespace NS1 -{ - static class E - { - extension(in T x) where T : struct - { - } - } -} - -namespace NS2 -{ - static class E - { - extension(ref readonly T x) where T : struct - { - } - } -} -"""; - - var comp2 = CreateCompilation(src2); - comp2.VerifyDiagnostics( - // (13,11): error CS1510: A ref or out value must be an assignable variable - // ++default(T).P1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 11), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); } [Fact] - public void PropertyAccess_PrefixIncrementAssignment_06() + public void PropertyAccess_CompoundAssignment_04() { var src = """ +using System.Threading.Tasks; + static class E { extension(T x) { - public S2 P1 + public int P1 { get { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return default; + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; } set { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(((S1)(object)x).F1); } } } } -class C1 +struct S1 { public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return x; - } + public static T F; } class Program { - public static C1 F; - - static void Main() + static async Task Main() { - F = new C1 { F1 = 123 }; - Test1(ref F); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new C1 { F1 = 123 }; - Test2(ref F); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - ++f.P1; + f.P1 += Get1(); } - static void Test2(ref T f) where T : class + static void Test2(ref T f) where T : struct { - ++f.P1; + f.P1 += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 61 (0x3d) - .maxstack 2 + // Code size 62 (0x3e) + .maxstack 3 .locals init (T V_0, T& V_1, - S2 V_2, - T V_3) + T V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 - IL_0003: ldloca.s V_3 + IL_0003: ldloca.s V_2 IL_0005: initobj ""T"" - IL_000b: ldloc.3 + IL_000b: ldloc.2 IL_000c: box ""T"" IL_0011: brtrue.s IL_001e IL_0013: ldloc.1 @@ -7059,57 +7005,62 @@ .locals init (T V_0, IL_001e: ldloc.1 IL_001f: dup IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 27 (0x1b) - .maxstack 2 - .locals init (S2 V_0) + // Code size 33 (0x21) + .maxstack 3 + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: dup - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: call ""S2 S2.op_Increment(S2)"" - IL_0012: stloc.0 - IL_0013: ldloc.0 - IL_0014: call ""void E.set_P1(T, S2)"" - IL_0019: nop - IL_001a: ret + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: call ""int E.get_P1(T)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldobj ""T"" + IL_0019: ldloc.0 + IL_001a: call ""void E.set_P1(T, int)"" + IL_001f: nop + IL_0020: ret } "); } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_01() + public void PropertyAccess_CompoundAssignment_05() { var src = """ +using System.Threading.Tasks; + static class E { - extension(S1 x) + extension(ref T x) where T : struct { - public S2 P1 + public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return default; + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -7118,42 +7069,49 @@ public S2 P1 struct S1 { public int F1; - - public void Test() - { - this.P1++; - } } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F.F1++; - return x; - } + public static T F; } class Program { - public static S1 F; - - static void Main() + static async Task Main() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } - static void Test() + static void Test2(ref T f) where T : struct { - F.P1++; + f.P1 += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; } } """; @@ -7161,231 +7119,96 @@ static void Test() var comp = CreateCompilation(src, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 36 (0x24) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: call ""S2 E.get_P1(S1)"" - IL_0011: call ""S2 S2.op_Increment(S2)"" - IL_0016: stloc.0 - IL_0017: ldobj ""S1"" - IL_001c: ldloc.0 - IL_001d: call ""void E.set_P1(S1, S2)"" - IL_0022: nop - IL_0023: ret -} -"); - - verifier.VerifyIL("S1.Test", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 32 (0x20) - .maxstack 2 - .locals init (S2 V_0) + // Code size 21 (0x15) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""S2 E.get_P1(S1)"" - IL_000c: call ""S2 S2.op_Increment(S2)"" - IL_0011: stloc.0 - IL_0012: ldarg.0 - IL_0013: ldobj ""S1"" - IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(S1, S2)"" - IL_001e: nop - IL_001f: ret + IL_0002: dup + IL_0003: call ""int E.get_P1(ref T)"" + IL_0008: call ""int Program.Get1()"" + IL_000d: add + IL_000e: call ""void E.set_P1(ref T, int)"" + IL_0013: nop + IL_0014: ret } "); - } - [Theory] - [InlineData("ref")] - [InlineData("ref readonly")] - [InlineData("in")] - public void PropertyAccess_PostfixIncrementAssignment_02(string refKind) - { - var src = $$$""" + var src2 = """ static class E { - extension({{{refKind}}} S1 x) + extension(ref T x) where T : struct { - public S2 P1 - { - get - { - System.Console.Write(x.F1); - Program.F.F1++; - return default; - } - set - { - System.Console.Write(x.F1); - } - } + public int P1 { get => 0; set {} } } } -struct S1 +class Program { - public int F1; - - public void Test() + static void Test() where T : struct { - this.P1++; + default(T).P1 += 1; } } -struct S2 +namespace NS1 { - public static S2 operator ++(S2 x) + static class E { - Program.F.F1++; - return x; + extension(in T x) where T : struct + { + } } } -class Program +namespace NS2 { - public static S1 F; - - static void Main() - { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); - } - - static void Test() + static class E { - F.P1++; - } -} -"""; - - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 26 (0x1a) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_000c: stloc.0 - IL_000d: ldloc.0 - IL_000e: call ""S2 S2.op_Increment(S2)"" - IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0018: nop - IL_0019: ret -} -"); - - verifier.VerifyIL("S1.Test", -@" -{ - // Code size 22 (0x16) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" - IL_0007: stloc.0 - IL_0008: ldarg.0 - IL_0009: ldloc.0 - IL_000a: call ""S2 S2.op_Increment(S2)"" - IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" - IL_0014: nop - IL_0015: ret -} -"); - - var src2 = $$$""" -static class E -{ - extension({{{refKind}}} S1 x) - { - public int P1 { get => 0; set {} } - } -} - -struct S1; - -class Program -{ - static void Test() - { - default(S1).P1++; + extension(ref readonly T x) where T : struct + { + } } } """; var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (15,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P1++; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P1++; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), - // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1++; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1++; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } + comp2.VerifyDiagnostics( + // (13,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P1 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_03() + public void PropertyAccess_CompoundAssignment_06() { var src = """ +using System.Threading.Tasks; + static class E { - extension(C1 x) + extension(T x) { - public S2 P1 + public int P1 { get { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return default; + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((C1)(object)x).F1); } } } @@ -7396,49 +7219,115 @@ class C1 public int F1; } -struct S2 +class Program { - public static S2 operator ++(S2 x) - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return x; - } + public static T F; } class Program { - public static C1 F = new C1 { F1 = 123 }; + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); - static void Main() + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) { - Test(); - System.Console.Write(F.F1); + f.P1 += Get1(); } - static void Test() + static void Test2(ref T f) where T : class { - F.P1++; + f.P1 += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 62 (0x3e) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", @" { // Code size 26 (0x1a) - .maxstack 2 - .locals init (S2 V_0) + .maxstack 3 IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: dup - IL_0007: call ""S2 E.get_P1(C1)"" - IL_000c: stloc.0 - IL_000d: ldloc.0 - IL_000e: call ""S2 S2.op_Increment(S2)"" - IL_0013: call ""void E.set_P1(C1, S2)"" + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: dup + IL_0008: call ""int E.get_P1(T)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: call ""void E.set_P1(T, int)"" IL_0018: nop IL_0019: ret } @@ -7446,24 +7335,24 @@ .locals init (S2 V_0) } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_04() + public void PropertyAccess_PrefixIncrementAssignment_01() { var src = """ static class E { - extension(T x) + extension(S1 x) { public S2 P1 { get { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); Program.F.F1++; return default; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); } } } @@ -7472,6 +7361,11 @@ public S2 P1 struct S1 { public int F1; + + public void Test() + { + ++this.P1; + } } struct S2 @@ -7490,24 +7384,19 @@ class Program static void Main() { F = new S1 { F1 = 123 }; - Test1(ref F); + Test(); System.Console.Write(F.F1); System.Console.Write(":"); F = new S1 { F1 = 123 }; - Test2(ref F); + F.Test(); System.Console.Write(F.F1); } - static void Test1(ref T f) - { - f.P1++; - } - - static void Test2(ref T f) where T : struct + static void Test() { - f.P1++; + ++F.P1; } } """; @@ -7515,43 +7404,28 @@ static void Test2(ref T f) where T : struct var comp = CreateCompilation(src, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 61 (0x3d) + // Code size 36 (0x24) .maxstack 2 - .locals init (T V_0, - T& V_1, - S2 V_2, - T V_3) + .locals init (S2 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: ldobj ""S1"" + IL_000c: call ""S2 E.get_P1(S1)"" + IL_0011: call ""S2 S2.op_Increment(S2)"" + IL_0016: stloc.0 + IL_0017: ldobj ""S1"" + IL_001c: ldloc.0 + IL_001d: call ""void E.set_P1(S1, S2)"" + IL_0022: nop + IL_0023: ret } "); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("S1.Test", @" { // Code size 32 (0x20) @@ -7559,39 +7433,42 @@ .maxstack 2 .locals init (S2 V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: ldobj ""T"" - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: call ""S2 S2.op_Increment(S2)"" - IL_0012: stloc.0 - IL_0013: ldobj ""T"" + IL_0002: ldobj ""S1"" + IL_0007: call ""S2 E.get_P1(S1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldarg.0 + IL_0013: ldobj ""S1"" IL_0018: ldloc.0 - IL_0019: call ""void E.set_P1(T, S2)"" + IL_0019: call ""void E.set_P1(S1, S2)"" IL_001e: nop IL_001f: ret } "); } - [Fact] - public void PropertyAccess_PostfixIncrementAssignment_05() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_PrefixIncrementAssignment_02(string refKind) { - var src = """ + var src = $$$""" static class E { - extension(ref T x) where T : struct + extension({{{refKind}}} S1 x) { public S2 P1 { get { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); Program.F.F1++; return default; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); } } } @@ -7600,6 +7477,11 @@ public S2 P1 struct S1 { public int F1; + + public void Test() + { + ++this.P1; + } } struct S2 @@ -7618,21 +7500,46 @@ class Program static void Main() { F = new S1 { F1 = 123 }; - Test2(ref F); + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); System.Console.Write(F.F1); } - static void Test2(ref T f) where T : struct + static void Test() { - f.P1++; + ++F.P1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 26 (0x1a) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" + IL_0018: nop + IL_0019: ret +} +"); + + verifier.VerifyIL("S1.Test", @" { // Code size 22 (0x16) @@ -7640,88 +7547,88 @@ .maxstack 2 .locals init (S2 V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: call ""S2 E.get_P1(ref T)"" - IL_0008: stloc.0 - IL_0009: ldloc.0 - IL_000a: call ""S2 S2.op_Increment(S2)"" - IL_000f: call ""void E.set_P1(ref T, S2)"" + IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_0007: call ""S2 S2.op_Increment(S2)"" + IL_000c: stloc.0 + IL_000d: ldarg.0 + IL_000e: ldloc.0 + IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" IL_0014: nop IL_0015: ret } "); - var src2 = """ + var src2 = $$$""" static class E { - extension(ref T x) where T : struct + extension({{{refKind}}} S1 x) { public int P1 { get => 0; set {} } } } -class Program -{ - static void Test() where T : struct - { - default(T).P1++; - } -} - -namespace NS1 -{ - static class E - { - extension(in T x) where T : struct - { - } - } -} +struct S1; -namespace NS2 +class Program { - static class E + static void Test() { - extension(ref readonly T x) where T : struct - { - } + ++default(S1).P1; } } """; var comp2 = CreateCompilation(src2); - comp2.VerifyDiagnostics( - // (13,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P1++; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,11): error CS1510: A ref or out value must be an assignable variable + // ++default(S1).P1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 11) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,11): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // ++default(S1).P1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 11), + // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // ++default(S1).P1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // ++default(S1).P1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 11) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } } [Fact] - public void PropertyAccess_PostfixIncrementAssignment_06() + public void PropertyAccess_PrefixIncrementAssignment_03() { var src = """ static class E { - extension(T x) + extension(C1 x) { public S2 P1 { get { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(x.F1); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return default; } set { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(x.F1); } } } @@ -7743,126 +7650,63 @@ struct S2 class Program { - public static C1 F; + public static C1 F = new C1 { F1 = 123 }; static void Main() { - F = new C1 { F1 = 123 }; - Test1(ref F); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new C1 { F1 = 123 }; - Test2(ref F); + Test(); System.Console.Write(F.F1); } - static void Test1(ref T f) - { - f.P1++; - } - - static void Test2(ref T f) where T : class + static void Test() { - f.P1++; + ++F.P1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 61 (0x3d) + // Code size 26 (0x1a) .maxstack 2 - .locals init (T V_0, - T& V_1, - S2 V_2, - T V_3) + .locals init (S2 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: call ""S2 E.get_P1(T)"" - IL_002a: call ""S2 S2.op_Increment(S2)"" - IL_002f: stloc.2 - IL_0030: ldobj ""T"" - IL_0035: ldloc.2 - IL_0036: call ""void E.set_P1(T, S2)"" - IL_003b: nop - IL_003c: ret -} -"); - - verifier.VerifyIL("Program.Test2(ref T)", -@" -{ - // Code size 27 (0x1b) - .maxstack 2 - .locals init (S2 V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: dup - IL_0008: call ""S2 E.get_P1(T)"" - IL_000d: stloc.0 - IL_000e: ldloc.0 - IL_000f: call ""S2 S2.op_Increment(S2)"" - IL_0014: call ""void E.set_P1(T, S2)"" - IL_0019: nop - IL_001a: ret + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(C1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: call ""void E.set_P1(C1, S2)"" + IL_0018: nop + IL_0019: ret } "); } [Fact] - public void PropertyAccess_ConditionalAssignment_01() + public void PropertyAccess_PrefixIncrementAssignment_04() { var src = """ static class E { - extension(S1 x) + extension(T x) { - public object P1 - { - get - { - System.Console.Write(x.F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(x.F1); - } - } - - public int? P2 + public S2 P1 { get { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); Program.F.F1++; - return null; + return default; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -7871,15 +7715,14 @@ public int? P2 struct S1 { public int F1; +} - public void Test1() - { - this.P1 ??= Program.Get1(); - } - - public void Test2() +struct S2 +{ + public static S2 operator ++(S2 x) { - this.P2 ??= Program.Get1(); + Program.F.F1++; + return x; } } @@ -7890,490 +7733,361 @@ class Program static void Main() { F = new S1 { F1 = 123 }; - Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - Test2(); + Test1(ref F); System.Console.Write(F.F1); System.Console.Write(":"); F = new S1 { F1 = 123 }; - F.Test2(); + Test2(ref F); System.Console.Write(F.F1); } - static void Test1() - { - F.P1 ??= Get1(); - } - - static void Test2() + static void Test1(ref T f) { - F.P2 ??= Get1(); + ++f.P1; } - public static int Get1() + static void Test2(ref T f) where T : struct { - Program.F.F1++; - return 1; + ++f.P1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1", + verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 47 (0x2f) - .maxstack 3 - .locals init (S1& V_0, - object V_1, - object V_2) + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""object E.get_P1(S1)"" - IL_0012: brtrue.s IL_002e - IL_0014: call ""int Program.Get1()"" - IL_0019: box ""int"" - IL_001e: stloc.1 - IL_001f: ldloc.0 - IL_0020: ldobj ""S1"" - IL_0025: ldloc.1 - IL_0026: dup - IL_0027: stloc.2 - IL_0028: call ""void E.set_P1(S1, object)"" - IL_002d: nop - IL_002e: ret + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret } "); - verifier.VerifyIL("S1.Test1", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 41 (0x29) - .maxstack 3 - .locals init (object V_0, - object V_1) + // Code size 32 (0x20) + .maxstack 2 + .locals init (S2 V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""object E.get_P1(S1)"" - IL_000c: brtrue.s IL_0028 - IL_000e: call ""int Program.Get1()"" - IL_0013: box ""int"" - IL_0018: stloc.0 - IL_0019: ldarg.0 - IL_001a: ldobj ""S1"" - IL_001f: ldloc.0 - IL_0020: dup - IL_0021: stloc.1 - IL_0022: call ""void E.set_P1(S1, object)"" - IL_0027: nop - IL_0028: ret + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: call ""S2 S2.op_Increment(S2)"" + IL_0012: stloc.0 + IL_0013: ldobj ""T"" + IL_0018: ldloc.0 + IL_0019: call ""void E.set_P1(T, S2)"" + IL_001e: nop + IL_001f: ret } "); + } - verifier.VerifyIL("Program.Test2", -@" + [Fact] + public void PropertyAccess_PrefixIncrementAssignment_05() + { + var src = """ +static class E { - // Code size 66 (0x42) - .maxstack 3 - .locals init (S1& V_0, - int? V_1, - int V_2, - int? V_3) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""int? E.get_P2(S1)"" - IL_0012: stloc.1 - IL_0013: ldloca.s V_1 - IL_0015: call ""int int?.GetValueOrDefault()"" - IL_001a: stloc.2 - IL_001b: ldloca.s V_1 - IL_001d: call ""bool int?.HasValue.get"" - IL_0022: brtrue.s IL_0041 - IL_0024: call ""int Program.Get1()"" - IL_0029: stloc.2 - IL_002a: ldloc.0 - IL_002b: ldobj ""S1"" - IL_0030: ldloca.s V_3 - IL_0032: ldloc.2 - IL_0033: call ""int?..ctor(int)"" - IL_0038: ldloc.3 - IL_0039: call ""void E.set_P2(S1, int?)"" - IL_003e: nop - IL_003f: br.s IL_0041 - IL_0041: ret + extension(ref T x) where T : struct + { + public S2 P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } } -"); - verifier.VerifyIL("S1.Test2", -@" +struct S1 { - // Code size 60 (0x3c) - .maxstack 3 - .locals init (int? V_0, - int V_1, - int? V_2) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""int? E.get_P2(S1)"" - IL_000c: stloc.0 - IL_000d: ldloca.s V_0 - IL_000f: call ""int int?.GetValueOrDefault()"" - IL_0014: stloc.1 - IL_0015: ldloca.s V_0 - IL_0017: call ""bool int?.HasValue.get"" - IL_001c: brtrue.s IL_003b - IL_001e: call ""int Program.Get1()"" - IL_0023: stloc.1 - IL_0024: ldarg.0 - IL_0025: ldobj ""S1"" - IL_002a: ldloca.s V_2 - IL_002c: ldloc.1 - IL_002d: call ""int?..ctor(int)"" - IL_0032: ldloc.2 - IL_0033: call ""void E.set_P2(S1, int?)"" - IL_0038: nop - IL_0039: br.s IL_003b - IL_003b: ret + public int F1; } -"); + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; } +} - [Theory] - [InlineData("ref")] - [InlineData("ref readonly")] - [InlineData("in")] - public void PropertyAccess_ConditionalAssignment_02(string refKind) +class Program +{ + public static S1 F; + + static void Main() { - var src = $$$""" + F = new S1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test2(ref T f) where T : struct + { + ++f.P1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 22 (0x16) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: call ""S2 E.get_P1(ref T)"" + IL_0008: call ""S2 S2.op_Increment(S2)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: call ""void E.set_P1(ref T, S2)"" + IL_0014: nop + IL_0015: ret +} +"); + + var src2 = """ static class E { - extension({{{refKind}}} S1 x) + extension(ref T x) where T : struct { - public object P1 + public int P1 { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + ++default(T).P1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct { - get - { - System.Console.Write(x.F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(x.F1); - } } - public int? P2 + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,11): error CS1510: A ref or out value must be an assignable variable + // ++default(T).P1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 11), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void PropertyAccess_PrefixIncrementAssignment_06() + { + var src = """ +static class E +{ + extension(T x) + { + public S2 P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; - return null; + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return default; } set { - System.Console.Write(x.F1); + System.Console.Write(((C1)(object)x).F1); } } } } -struct S1 +class C1 { public int F1; +} - public void Test1() - { - this.P1 ??= Program.Get1(); - } - - public void Test2() +struct S2 +{ + public static S2 operator ++(S2 x) { - this.P2 ??= Program.Get1(); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return x; } } class Program { - public static S1 F; + public static C1 F; static void Main() { - F = new S1 { F1 = 123 }; - Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test1(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - Test2(); + F = new C1 { F1 = 123 }; + Test1(ref F); System.Console.Write(F.F1); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - F.Test2(); + F = new C1 { F1 = 123 }; + Test2(ref F); System.Console.Write(F.F1); } - static void Test1() - { - F.P1 ??= Get1(); - } - - static void Test2() + static void Test1(ref T f) { - F.P2 ??= Get1(); + ++f.P1; } - public static int Get1() + static void Test2(ref T f) where T : class { - Program.F.F1++; - return 1; + ++f.P1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1", + verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 35 (0x23) - .maxstack 3 - .locals init (S1& V_0, - object V_1) + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""object E.get_P1(" + refKind + @" S1)"" - IL_000d: brtrue.s IL_0022 - IL_000f: ldloc.0 - IL_0010: call ""int Program.Get1()"" - IL_0015: box ""int"" - IL_001a: dup - IL_001b: stloc.1 - IL_001c: call ""void E.set_P1(" + refKind + @" S1, object)"" - IL_0021: nop - IL_0022: ret + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret } "); - verifier.VerifyIL("S1.Test1", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 29 (0x1d) - .maxstack 3 - .locals init (object V_0) + // Code size 27 (0x1b) + .maxstack 2 + .locals init (S2 V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: call ""object E.get_P1(" + refKind + @" S1)"" - IL_0007: brtrue.s IL_001c - IL_0009: ldarg.0 - IL_000a: call ""int Program.Get1()"" - IL_000f: box ""int"" - IL_0014: dup - IL_0015: stloc.0 - IL_0016: call ""void E.set_P1(" + refKind + @" S1, object)"" - IL_001b: nop - IL_001c: ret -} -"); - - verifier.VerifyIL("Program.Test2", -@" -{ - // Code size 56 (0x38) - .maxstack 3 - .locals init (S1& V_0, - int? V_1, - int V_2, - int? V_3) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int? E.get_P2(" + refKind + @" S1)"" - IL_000d: stloc.1 - IL_000e: ldloca.s V_1 - IL_0010: call ""int int?.GetValueOrDefault()"" - IL_0015: stloc.2 - IL_0016: ldloca.s V_1 - IL_0018: call ""bool int?.HasValue.get"" - IL_001d: brtrue.s IL_0037 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.2 - IL_0025: ldloc.0 - IL_0026: ldloca.s V_3 - IL_0028: ldloc.2 - IL_0029: call ""int?..ctor(int)"" - IL_002e: ldloc.3 - IL_002f: call ""void E.set_P2(" + refKind + @" S1, int?)"" - IL_0034: nop - IL_0035: br.s IL_0037 - IL_0037: ret -} -"); - - verifier.VerifyIL("S1.Test2", -@" -{ - // Code size 50 (0x32) - .maxstack 3 - .locals init (int? V_0, - int V_1, - int? V_2) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""int? E.get_P2(" + refKind + @" S1)"" - IL_0007: stloc.0 - IL_0008: ldloca.s V_0 - IL_000a: call ""int int?.GetValueOrDefault()"" - IL_000f: stloc.1 - IL_0010: ldloca.s V_0 - IL_0012: call ""bool int?.HasValue.get"" - IL_0017: brtrue.s IL_0031 - IL_0019: call ""int Program.Get1()"" - IL_001e: stloc.1 - IL_001f: ldarg.0 - IL_0020: ldloca.s V_2 - IL_0022: ldloc.1 - IL_0023: call ""int?..ctor(int)"" - IL_0028: ldloc.2 - IL_0029: call ""void E.set_P2(" + refKind + @" S1, int?)"" - IL_002e: nop - IL_002f: br.s IL_0031 - IL_0031: ret + IL_0002: ldobj ""T"" + IL_0007: dup + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: call ""S2 S2.op_Increment(S2)"" + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: call ""void E.set_P1(T, S2)"" + IL_0019: nop + IL_001a: ret } "); - - var src2 = $$$""" -static class E -{ - extension({{{refKind}}} S1 x) - { - public object P1 { get => 0; set {} } - public int? P2 { get => 0; set {} } - } -} - -struct S1; - -class Program -{ - static void Test1() - { - default(S1).P1 ??= 1; - } - static void Test2() - { - default(S1).P2 ??= 1; - } -} -"""; - - var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (16,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), - // (20,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(20, 9) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), - // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), - // (20,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(20, 9), - // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), - // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P2 ??= 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } } [Fact] - public void PropertyAccess_ConditionalAssignment_03() + public void PropertyAccess_PostfixIncrementAssignment_01() { var src = """ static class E { - extension(C1 x) + extension(S1 x) { - public object P1 - { - get - { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; - } - set - { - System.Console.Write(x.F1); - } - } - public int? P2 + public S2 P1 { get { System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; + Program.F.F1++; + return default; } set { @@ -8383,143 +8097,117 @@ public int? P2 } } -class C1 +struct S1 { public int F1; + + public void Test() + { + this.P1++; + } +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } } class Program { - public static C1 F; + public static S1 F; static void Main() { - F = new C1 { F1 = 123 }; - Test1(); + F = new S1 { F1 = 123 }; + Test(); System.Console.Write(F.F1); System.Console.Write(":"); - F = new C1 { F1 = 123 }; - Test2(); + F = new S1 { F1 = 123 }; + F.Test(); System.Console.Write(F.F1); } - static void Test1() - { - F.P1 ??= Get1(); - } - - static void Test2() - { - F.P2 ??= Get1(); - } - - static int Get1() + static void Test() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return 1; + F.P1++; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1", + verifier.VerifyIL("Program.Test", @" { - // Code size 35 (0x23) - .maxstack 3 - .locals init (C1 V_0, - object V_1) + // Code size 36 (0x24) + .maxstack 2 + .locals init (S2 V_0) IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""object E.get_P1(C1)"" - IL_000d: brtrue.s IL_0022 - IL_000f: ldloc.0 - IL_0010: call ""int Program.Get1()"" - IL_0015: box ""int"" - IL_001a: dup - IL_001b: stloc.1 - IL_001c: call ""void E.set_P1(C1, object)"" - IL_0021: nop - IL_0022: ret + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: ldobj ""S1"" + IL_000c: call ""S2 E.get_P1(S1)"" + IL_0011: call ""S2 S2.op_Increment(S2)"" + IL_0016: stloc.0 + IL_0017: ldobj ""S1"" + IL_001c: ldloc.0 + IL_001d: call ""void E.set_P1(S1, S2)"" + IL_0022: nop + IL_0023: ret } "); - verifier.VerifyIL("Program.Test2", + verifier.VerifyIL("S1.Test", @" { - // Code size 56 (0x38) - .maxstack 3 - .locals init (C1 V_0, - int? V_1, - int V_2, - int? V_3) + // Code size 32 (0x20) + .maxstack 2 + .locals init (S2 V_0) IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int? E.get_P2(C1)"" - IL_000d: stloc.1 - IL_000e: ldloca.s V_1 - IL_0010: call ""int int?.GetValueOrDefault()"" - IL_0015: stloc.2 - IL_0016: ldloca.s V_1 - IL_0018: call ""bool int?.HasValue.get"" - IL_001d: brtrue.s IL_0037 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.2 - IL_0025: ldloc.0 - IL_0026: ldloca.s V_3 - IL_0028: ldloc.2 - IL_0029: call ""int?..ctor(int)"" - IL_002e: ldloc.3 - IL_002f: call ""void E.set_P2(C1, int?)"" - IL_0034: nop - IL_0035: br.s IL_0037 - IL_0037: ret + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""S2 E.get_P1(S1)"" + IL_000c: call ""S2 S2.op_Increment(S2)"" + IL_0011: stloc.0 + IL_0012: ldarg.0 + IL_0013: ldobj ""S1"" + IL_0018: ldloc.0 + IL_0019: call ""void E.set_P1(S1, S2)"" + IL_001e: nop + IL_001f: ret } "); } - [Fact] - public void PropertyAccess_ConditionalAssignment_04() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_PostfixIncrementAssignment_02(string refKind) { - var src = """ -using System.Threading.Tasks; - + var src = $$$""" static class E { - extension(T x) + extension({{{refKind}}} S1 x) { - public object P1 - { - get - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(((S1)(object)x).F1); - } - } - public int? P2 + public S2 P1 { get { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; + System.Console.Write(x.F1); + Program.F.F1++; + return default; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); } } } @@ -8528,215 +8216,2037 @@ public int? P2 struct S1 { public int F1; -} - - -class Program -{ - public static T F; -} - -class Program -{ - static async Task Main() - { - Program.F = new S1 { F1 = 123 }; - Test11(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - Test12(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - await Test13(); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - Test21(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - Test22(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - await Test23(); - System.Console.Write(Program.F.F1); - } - - static void Test11(ref T f) - { - f.P1 ??= Get1(); - } - static void Test21(ref T f) + public void Test() { - f.P2 ??= Get1(); + this.P1++; } +} - static void Test12(ref T f) where T : struct +struct S2 +{ + public static S2 operator ++(S2 x) { - f.P1 ??= Get1(); + Program.F.F1++; + return x; } +} - static void Test22(ref T f) where T : struct - { - f.P2 ??= Get1(); - } +class Program +{ + public static S1 F; - static int Get1() + static void Main() { - Program.F.F1++; - return 1; - } + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); - static async Task Test13() - { - Program.F.P1 ??= await Get1Async(); - } + System.Console.Write(":"); - static async Task Test23() - { - Program.F.P2 ??= await Get1Async(); + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); } - static async Task Get1Async() + static void Test() { - Program.F.F1++; - await Task.Yield(); - return 1; + F.P1++; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125:123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test11(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 75 (0x4b) - .maxstack 3 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3, - object V_4, - object V_5) + // Code size 26 (0x1a) + .maxstack 2 + .locals init (S2 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""object E.get_P1(T)"" - IL_002b: brtrue.s IL_004a - IL_002d: call ""int Program.Get1()"" - IL_0032: box ""int"" - IL_0037: stloc.s V_4 - IL_0039: ldloc.0 - IL_003a: ldobj ""T"" - IL_003f: ldloc.s V_4 - IL_0041: dup - IL_0042: stloc.s V_5 - IL_0044: call ""void E.set_P1(T, object)"" - IL_0049: nop - IL_004a: ret + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: call ""S2 S2.op_Increment(S2)"" + IL_0013: call ""void E.set_P1(" + refKind + @" S1, S2)"" + IL_0018: nop + IL_0019: ret } "); - verifier.VerifyIL("Program.Test12(ref T)", + verifier.VerifyIL("S1.Test", @" { - // Code size 43 (0x2b) - .maxstack 3 - .locals init (T& V_0, - object V_1, - object V_2) + // Code size 22 (0x16) + .maxstack 2 + .locals init (S2 V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""object E.get_P1(T)"" - IL_000e: brtrue.s IL_002a - IL_0010: call ""int Program.Get1()"" - IL_0015: box ""int"" - IL_001a: stloc.1 - IL_001b: ldloc.0 - IL_001c: ldobj ""T"" - IL_0021: ldloc.1 - IL_0022: dup - IL_0023: stloc.2 - IL_0024: call ""void E.set_P1(T, object)"" - IL_0029: nop - IL_002a: ret -} -"); - - verifier.VerifyIL("Program.Test21(ref T)", -@" + IL_0002: call ""S2 E.get_P1(" + refKind + @" S1)"" + IL_0007: stloc.0 + IL_0008: ldarg.0 + IL_0009: ldloc.0 + IL_000a: call ""S2 S2.op_Increment(S2)"" + IL_000f: call ""void E.set_P1(" + refKind + @" S1, S2)"" + IL_0014: nop + IL_0015: ret +} +"); + + var src2 = $$$""" +static class E { - // Code size 96 (0x60) - .maxstack 3 - .locals init (T& V_0, - T V_1, - T& V_2, - int? V_3, - int V_4, - T V_5, - int? V_6) + extension({{{refKind}}} S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1).P1++; + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P1++; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P1++; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1++; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1++; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public S2 P1 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return default; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return x; + } +} + +class Program +{ + public static C1 F = new C1 { F1 = 123 }; + + static void Main() + { + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 26 (0x1a) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: dup + IL_0007: call ""S2 E.get_P1(C1)"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: call ""S2 S2.op_Increment(S2)"" + IL_0013: call ""void E.set_P1(C1, S2)"" + IL_0018: nop + IL_0019: ret +} +"); + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_04() + { + var src = """ +static class E +{ + extension(T x) + { + public S2 P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test1(ref F); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test1(ref T f) + { + f.P1++; + } + + static void Test2(ref T f) where T : struct + { + f.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_5 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 IL_0005: initobj ""T"" - IL_000b: ldloc.s V_5 - IL_000d: box ""T"" - IL_0012: brtrue.s IL_001f - IL_0014: ldloc.2 - IL_0015: ldobj ""T"" - IL_001a: stloc.1 - IL_001b: ldloca.s V_1 - IL_001d: br.s IL_0020 - IL_001f: ldloc.2 - IL_0020: stloc.0 - IL_0021: ldloc.0 - IL_0022: ldobj ""T"" - IL_0027: call ""int? E.get_P2(T)"" - IL_002c: stloc.3 - IL_002d: ldloca.s V_3 - IL_002f: call ""int int?.GetValueOrDefault()"" - IL_0034: stloc.s V_4 - IL_0036: ldloca.s V_3 - IL_0038: call ""bool int?.HasValue.get"" - IL_003d: brtrue.s IL_005f - IL_003f: call ""int Program.Get1()"" - IL_0044: stloc.s V_4 - IL_0046: ldloc.0 - IL_0047: ldobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: call ""S2 S2.op_Increment(S2)"" + IL_0012: stloc.0 + IL_0013: ldobj ""T"" + IL_0018: ldloc.0 + IL_0019: call ""void E.set_P1(T, S2)"" + IL_001e: nop + IL_001f: ret +} +"); + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_05() + { + var src = """ +static class E +{ + extension(ref T x) where T : struct + { + public S2 P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return default; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F.F1++; + return x; + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test2(ref T f) where T : struct + { + f.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 22 (0x16) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: call ""S2 E.get_P1(ref T)"" + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: call ""S2 S2.op_Increment(S2)"" + IL_000f: call ""void E.set_P1(ref T, S2)"" + IL_0014: nop + IL_0015: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int P1 { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T).P1++; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P1++; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void PropertyAccess_PostfixIncrementAssignment_06() + { + var src = """ +static class E +{ + extension(T x) + { + public S2 P1 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return default; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +struct S2 +{ + public static S2 operator ++(S2 x) + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return x; + } +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test1(ref F); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new C1 { F1 = 123 }; + Test2(ref F); + System.Console.Write(F.F1); + } + + static void Test1(ref T f) + { + f.P1++; + } + + static void Test2(ref T f) where T : class + { + f.P1++; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 61 (0x3d) + .maxstack 2 + .locals init (T V_0, + T& V_1, + S2 V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""S2 E.get_P1(T)"" + IL_002a: call ""S2 S2.op_Increment(S2)"" + IL_002f: stloc.2 + IL_0030: ldobj ""T"" + IL_0035: ldloc.2 + IL_0036: call ""void E.set_P1(T, S2)"" + IL_003b: nop + IL_003c: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 27 (0x1b) + .maxstack 2 + .locals init (S2 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: dup + IL_0008: call ""S2 E.get_P1(T)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: call ""S2 S2.op_Increment(S2)"" + IL_0014: call ""void E.set_P1(T, S2)"" + IL_0019: nop + IL_001a: ret +} +"); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public object P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + + public int? P2 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test1() + { + this.P1 ??= Program.Get1(); + } + + public void Test2() + { + this.P2 ??= Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + Test2(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test2(); + System.Console.Write(F.F1); + } + + static void Test1() + { + F.P1 ??= Get1(); + } + + static void Test2() + { + F.P2 ??= Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", +@" +{ + // Code size 47 (0x2f) + .maxstack 3 + .locals init (S1& V_0, + object V_1, + object V_2) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""object E.get_P1(S1)"" + IL_0012: brtrue.s IL_002e + IL_0014: call ""int Program.Get1()"" + IL_0019: box ""int"" + IL_001e: stloc.1 + IL_001f: ldloc.0 + IL_0020: ldobj ""S1"" + IL_0025: ldloc.1 + IL_0026: dup + IL_0027: stloc.2 + IL_0028: call ""void E.set_P1(S1, object)"" + IL_002d: nop + IL_002e: ret +} +"); + + verifier.VerifyIL("S1.Test1", +@" +{ + // Code size 41 (0x29) + .maxstack 3 + .locals init (object V_0, + object V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""object E.get_P1(S1)"" + IL_000c: brtrue.s IL_0028 + IL_000e: call ""int Program.Get1()"" + IL_0013: box ""int"" + IL_0018: stloc.0 + IL_0019: ldarg.0 + IL_001a: ldobj ""S1"" + IL_001f: ldloc.0 + IL_0020: dup + IL_0021: stloc.1 + IL_0022: call ""void E.set_P1(S1, object)"" + IL_0027: nop + IL_0028: ret +} +"); + + verifier.VerifyIL("Program.Test2", +@" +{ + // Code size 66 (0x42) + .maxstack 3 + .locals init (S1& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""int? E.get_P2(S1)"" + IL_0012: stloc.1 + IL_0013: ldloca.s V_1 + IL_0015: call ""int int?.GetValueOrDefault()"" + IL_001a: stloc.2 + IL_001b: ldloca.s V_1 + IL_001d: call ""bool int?.HasValue.get"" + IL_0022: brtrue.s IL_0041 + IL_0024: call ""int Program.Get1()"" + IL_0029: stloc.2 + IL_002a: ldloc.0 + IL_002b: ldobj ""S1"" + IL_0030: ldloca.s V_3 + IL_0032: ldloc.2 + IL_0033: call ""int?..ctor(int)"" + IL_0038: ldloc.3 + IL_0039: call ""void E.set_P2(S1, int?)"" + IL_003e: nop + IL_003f: br.s IL_0041 + IL_0041: ret +} +"); + + verifier.VerifyIL("S1.Test2", +@" +{ + // Code size 60 (0x3c) + .maxstack 3 + .locals init (int? V_0, + int V_1, + int? V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""int? E.get_P2(S1)"" + IL_000c: stloc.0 + IL_000d: ldloca.s V_0 + IL_000f: call ""int int?.GetValueOrDefault()"" + IL_0014: stloc.1 + IL_0015: ldloca.s V_0 + IL_0017: call ""bool int?.HasValue.get"" + IL_001c: brtrue.s IL_003b + IL_001e: call ""int Program.Get1()"" + IL_0023: stloc.1 + IL_0024: ldarg.0 + IL_0025: ldobj ""S1"" + IL_002a: ldloca.s V_2 + IL_002c: ldloc.1 + IL_002d: call ""int?..ctor(int)"" + IL_0032: ldloc.2 + IL_0033: call ""void E.set_P2(S1, int?)"" + IL_0038: nop + IL_0039: br.s IL_003b + IL_003b: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_ConditionalAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public object P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + public int? P2 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test1() + { + this.P1 ??= Program.Get1(); + } + + public void Test2() + { + this.P2 ??= Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + Test2(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test2(); + System.Console.Write(F.F1); + } + + static void Test1() + { + F.P1 ??= Get1(); + } + + static void Test2() + { + F.P2 ??= Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (S1& V_0, + object V_1) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""object E.get_P1(" + refKind + @" S1)"" + IL_000d: brtrue.s IL_0022 + IL_000f: ldloc.0 + IL_0010: call ""int Program.Get1()"" + IL_0015: box ""int"" + IL_001a: dup + IL_001b: stloc.1 + IL_001c: call ""void E.set_P1(" + refKind + @" S1, object)"" + IL_0021: nop + IL_0022: ret +} +"); + + verifier.VerifyIL("S1.Test1", +@" +{ + // Code size 29 (0x1d) + .maxstack 3 + .locals init (object V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""object E.get_P1(" + refKind + @" S1)"" + IL_0007: brtrue.s IL_001c + IL_0009: ldarg.0 + IL_000a: call ""int Program.Get1()"" + IL_000f: box ""int"" + IL_0014: dup + IL_0015: stloc.0 + IL_0016: call ""void E.set_P1(" + refKind + @" S1, object)"" + IL_001b: nop + IL_001c: ret +} +"); + + verifier.VerifyIL("Program.Test2", +@" +{ + // Code size 56 (0x38) + .maxstack 3 + .locals init (S1& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int? E.get_P2(" + refKind + @" S1)"" + IL_000d: stloc.1 + IL_000e: ldloca.s V_1 + IL_0010: call ""int int?.GetValueOrDefault()"" + IL_0015: stloc.2 + IL_0016: ldloca.s V_1 + IL_0018: call ""bool int?.HasValue.get"" + IL_001d: brtrue.s IL_0037 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldloc.0 + IL_0026: ldloca.s V_3 + IL_0028: ldloc.2 + IL_0029: call ""int?..ctor(int)"" + IL_002e: ldloc.3 + IL_002f: call ""void E.set_P2(" + refKind + @" S1, int?)"" + IL_0034: nop + IL_0035: br.s IL_0037 + IL_0037: ret +} +"); + + verifier.VerifyIL("S1.Test2", +@" +{ + // Code size 50 (0x32) + .maxstack 3 + .locals init (int? V_0, + int V_1, + int? V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int? E.get_P2(" + refKind + @" S1)"" + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: call ""int int?.GetValueOrDefault()"" + IL_000f: stloc.1 + IL_0010: ldloca.s V_0 + IL_0012: call ""bool int?.HasValue.get"" + IL_0017: brtrue.s IL_0031 + IL_0019: call ""int Program.Get1()"" + IL_001e: stloc.1 + IL_001f: ldarg.0 + IL_0020: ldloca.s V_2 + IL_0022: ldloc.1 + IL_0023: call ""int?..ctor(int)"" + IL_0028: ldloc.2 + IL_0029: call ""void E.set_P2(" + refKind + @" S1, int?)"" + IL_002e: nop + IL_002f: br.s IL_0031 + IL_0031: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public object P1 { get => 0; set {} } + public int? P2 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test1() + { + default(S1).P1 ??= 1; + } + static void Test2() + { + default(S1).P2 ??= 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (16,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), + // (20,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(20, 9) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), + // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), + // (20,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(20, 9), + // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(16, 9), + // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P2 ??= 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P2").WithLocation(20, 9) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public object P1 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + public int? P2 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test1(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new C1 { F1 = 123 }; + Test2(); + System.Console.Write(F.F1); + } + + static void Test1() + { + F.P1 ??= Get1(); + } + + static void Test2() + { + F.P2 ??= Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (C1 V_0, + object V_1) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""object E.get_P1(C1)"" + IL_000d: brtrue.s IL_0022 + IL_000f: ldloc.0 + IL_0010: call ""int Program.Get1()"" + IL_0015: box ""int"" + IL_001a: dup + IL_001b: stloc.1 + IL_001c: call ""void E.set_P1(C1, object)"" + IL_0021: nop + IL_0022: ret +} +"); + + verifier.VerifyIL("Program.Test2", +@" +{ + // Code size 56 (0x38) + .maxstack 3 + .locals init (C1 V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int? E.get_P2(C1)"" + IL_000d: stloc.1 + IL_000e: ldloca.s V_1 + IL_0010: call ""int int?.GetValueOrDefault()"" + IL_0015: stloc.2 + IL_0016: ldloca.s V_1 + IL_0018: call ""bool int?.HasValue.get"" + IL_001d: brtrue.s IL_0037 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldloc.0 + IL_0026: ldloca.s V_3 + IL_0028: ldloc.2 + IL_0029: call ""int?..ctor(int)"" + IL_002e: ldloc.3 + IL_002f: call ""void E.set_P2(C1, int?)"" + IL_0034: nop + IL_0035: br.s IL_0037 + IL_0037: ret +} +"); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public object P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + public int? P2 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test11(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test12(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test13(); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test21(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test22(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test23(); + System.Console.Write(Program.F.F1); + } + + static void Test11(ref T f) + { + f.P1 ??= Get1(); + } + + static void Test21(ref T f) + { + f.P2 ??= Get1(); + } + + static void Test12(ref T f) where T : struct + { + f.P1 ??= Get1(); + } + + static void Test22(ref T f) where T : struct + { + f.P2 ??= Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test13() + { + Program.F.P1 ??= await Get1Async(); + } + + static async Task Test23() + { + Program.F.P2 ??= await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test11(ref T)", +@" +{ + // Code size 75 (0x4b) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3, + object V_4, + object V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""object E.get_P1(T)"" + IL_002b: brtrue.s IL_004a + IL_002d: call ""int Program.Get1()"" + IL_0032: box ""int"" + IL_0037: stloc.s V_4 + IL_0039: ldloc.0 + IL_003a: ldobj ""T"" + IL_003f: ldloc.s V_4 + IL_0041: dup + IL_0042: stloc.s V_5 + IL_0044: call ""void E.set_P1(T, object)"" + IL_0049: nop + IL_004a: ret +} +"); + + verifier.VerifyIL("Program.Test12(ref T)", +@" +{ + // Code size 43 (0x2b) + .maxstack 3 + .locals init (T& V_0, + object V_1, + object V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""object E.get_P1(T)"" + IL_000e: brtrue.s IL_002a + IL_0010: call ""int Program.Get1()"" + IL_0015: box ""int"" + IL_001a: stloc.1 + IL_001b: ldloc.0 + IL_001c: ldobj ""T"" + IL_0021: ldloc.1 + IL_0022: dup + IL_0023: stloc.2 + IL_0024: call ""void E.set_P1(T, object)"" + IL_0029: nop + IL_002a: ret +} +"); + + verifier.VerifyIL("Program.Test21(ref T)", +@" +{ + // Code size 96 (0x60) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + int? V_3, + int V_4, + T V_5, + int? V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: ldloc.0 + IL_0022: ldobj ""T"" + IL_0027: call ""int? E.get_P2(T)"" + IL_002c: stloc.3 + IL_002d: ldloca.s V_3 + IL_002f: call ""int int?.GetValueOrDefault()"" + IL_0034: stloc.s V_4 + IL_0036: ldloca.s V_3 + IL_0038: call ""bool int?.HasValue.get"" + IL_003d: brtrue.s IL_005f + IL_003f: call ""int Program.Get1()"" + IL_0044: stloc.s V_4 + IL_0046: ldloc.0 + IL_0047: ldobj ""T"" + IL_004c: ldloca.s V_6 + IL_004e: ldloc.s V_4 + IL_0050: call ""int?..ctor(int)"" + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_P2(T, int?)"" + IL_005c: nop + IL_005d: br.s IL_005f + IL_005f: ret +} +"); + + verifier.VerifyIL("Program.Test22(ref T)", +@" +{ + // Code size 62 (0x3e) + .maxstack 3 + .locals init (T& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""int? E.get_P2(T)"" + IL_000e: stloc.1 + IL_000f: ldloca.s V_1 + IL_0011: call ""int int?.GetValueOrDefault()"" + IL_0016: stloc.2 + IL_0017: ldloca.s V_1 + IL_0019: call ""bool int?.HasValue.get"" + IL_001e: brtrue.s IL_003d + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: ldloc.0 + IL_0027: ldobj ""T"" + IL_002c: ldloca.s V_3 + IL_002e: ldloc.2 + IL_002f: call ""int?..ctor(int)"" + IL_0034: ldloc.3 + IL_0035: call ""void E.set_P2(T, int?)"" + IL_003a: nop + IL_003b: br.s IL_003d + IL_003d: ret +} +"); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public object P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + public int? P2 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test12(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test13(); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test22(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test23(); + System.Console.Write(Program.F.F1); + } + + static void Test12(ref T f) where T : struct + { + f.P1 ??= Get1(); + } + + static void Test22(ref T f) where T : struct + { + f.P2 ??= Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test13() where T : struct + { + Program.F.P1 ??= await Get1Async(); + } + + static async Task Test23() where T : struct + { + Program.F.P2 ??= await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test12(ref T)", +@" +{ + // Code size 31 (0x1f) + .maxstack 3 + .locals init (T& V_0, + object V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""object E.get_P1(ref T)"" + IL_0009: brtrue.s IL_001e + IL_000b: ldloc.0 + IL_000c: call ""int Program.Get1()"" + IL_0011: box ""int"" + IL_0016: dup + IL_0017: stloc.1 + IL_0018: call ""void E.set_P1(ref T, object)"" + IL_001d: nop + IL_001e: ret +} +"); + + verifier.VerifyIL("Program.Test22(ref T)", +@" +{ + // Code size 52 (0x34) + .maxstack 3 + .locals init (T& V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int? E.get_P2(ref T)"" + IL_0009: stloc.1 + IL_000a: ldloca.s V_1 + IL_000c: call ""int int?.GetValueOrDefault()"" + IL_0011: stloc.2 + IL_0012: ldloca.s V_1 + IL_0014: call ""bool int?.HasValue.get"" + IL_0019: brtrue.s IL_0033 + IL_001b: call ""int Program.Get1()"" + IL_0020: stloc.2 + IL_0021: ldloc.0 + IL_0022: ldloca.s V_3 + IL_0024: ldloc.2 + IL_0025: call ""int?..ctor(int)"" + IL_002a: ldloc.3 + IL_002b: call ""void E.set_P2(ref T, int?)"" + IL_0030: nop + IL_0031: br.s IL_0033 + IL_0033: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public object P1 { get => 0; set {} } + public int? P2 { get => 0; set {} } + } +} + +class Program +{ + static void Test1() where T : struct + { + default(T).P1 += 1; + } + static void Test2() where T : struct + { + default(T).P2 += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (14,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P1 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), + // (18,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P2 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(18, 9), + // (26,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(26, 25), + // (36,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(36, 35) + ); + } + + [Fact] + public void PropertyAccess_ConditionalAssignment_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public object P1 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + public int? P2 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return null; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test11(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test12(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test13(); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test21(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test22(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test23(); + System.Console.Write(Program.F.F1); + } + + static void Test11(ref T f) + { + f.P1 ??= Get1(); + } + + static void Test21(ref T f) + { + f.P2 ??= Get1(); + } + + static void Test12(ref T f) where T : class + { + f.P1 ??= Get1(); + } + + static void Test22(ref T f) where T : class + { + f.P2 ??= Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test13() + { + Program.F.P1 ??= await Get1Async(); + } + + static async Task Test23() + { + Program.F.P2 ??= await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125:123123125:123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test11(ref T)", +@" +{ + // Code size 75 (0x4b) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3, + object V_4, + object V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""object E.get_P1(T)"" + IL_002b: brtrue.s IL_004a + IL_002d: call ""int Program.Get1()"" + IL_0032: box ""int"" + IL_0037: stloc.s V_4 + IL_0039: ldloc.0 + IL_003a: ldobj ""T"" + IL_003f: ldloc.s V_4 + IL_0041: dup + IL_0042: stloc.s V_5 + IL_0044: call ""void E.set_P1(T, object)"" + IL_0049: nop + IL_004a: ret +} +"); + + verifier.VerifyIL("Program.Test12(ref T)", +@" +{ + // Code size 36 (0x24) + .maxstack 3 + .locals init (T V_0, + object V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""object E.get_P1(T)"" + IL_000e: brtrue.s IL_0023 + IL_0010: ldloc.0 + IL_0011: call ""int Program.Get1()"" + IL_0016: box ""int"" + IL_001b: dup + IL_001c: stloc.1 + IL_001d: call ""void E.set_P1(T, object)"" + IL_0022: nop + IL_0023: ret +} +"); + + verifier.VerifyIL("Program.Test21(ref T)", +@" +{ + // Code size 96 (0x60) + .maxstack 3 + .locals init (T& V_0, + T V_1, + T& V_2, + int? V_3, + int V_4, + T V_5, + int? V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: ldloc.0 + IL_0022: ldobj ""T"" + IL_0027: call ""int? E.get_P2(T)"" + IL_002c: stloc.3 + IL_002d: ldloca.s V_3 + IL_002f: call ""int int?.GetValueOrDefault()"" + IL_0034: stloc.s V_4 + IL_0036: ldloca.s V_3 + IL_0038: call ""bool int?.HasValue.get"" + IL_003d: brtrue.s IL_005f + IL_003f: call ""int Program.Get1()"" + IL_0044: stloc.s V_4 + IL_0046: ldloc.0 + IL_0047: ldobj ""T"" IL_004c: ldloca.s V_6 IL_004e: ldloc.s V_4 IL_0050: call ""int?..ctor(int)"" @@ -8746,80 +10256,2176 @@ .locals init (T& V_0, IL_005d: br.s IL_005f IL_005f: ret } -"); +"); + + verifier.VerifyIL("Program.Test22(ref T)", +@" +{ + // Code size 57 (0x39) + .maxstack 3 + .locals init (T V_0, + int? V_1, + int V_2, + int? V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int? E.get_P2(T)"" + IL_000e: stloc.1 + IL_000f: ldloca.s V_1 + IL_0011: call ""int int?.GetValueOrDefault()"" + IL_0016: stloc.2 + IL_0017: ldloca.s V_1 + IL_0019: call ""bool int?.HasValue.get"" + IL_001e: brtrue.s IL_0038 + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: ldloc.0 + IL_0027: ldloca.s V_3 + IL_0029: ldloc.2 + IL_002a: call ""int?..ctor(int)"" + IL_002f: ldloc.3 + IL_0030: call ""void E.set_P2(T, int?)"" + IL_0035: nop + IL_0036: br.s IL_0038 + IL_0038: ret +} +"); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + (this.P1, _) = (Program.Get1(), 0); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + (F.P1, _) = (Get1(), 0); + } + + public static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 25 (0x19) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldobj ""S1"" + IL_0011: ldloc.0 + IL_0012: call ""void E.set_P1(S1, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: call ""int Program.Get1()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(S1, int)"" + IL_0013: nop + IL_0014: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_DeconstructAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + (this.P1, _) = (Program.Get1(), 0); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + (F.P1, _) = (Get1(), 0); + } + + public static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 20 (0x14) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldloc.0 + IL_000d: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0012: nop + IL_0013: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: call ""int Program.Get1()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldloc.0 + IL_0009: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_000e: nop + IL_000f: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + (default(S1).P1, _) = (1, 0); + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,10): error CS1510: A ref or out value must be an assignable variable + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 10) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,10): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 10), + // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (default(S1).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F = new C1 { F1 = 123 }; + + static void Main() + { + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + (F.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 20 (0x14) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldloc.0 + IL_000d: call ""void E.set_P1(C1, int)"" + IL_0012: nop + IL_0013: ret +} +"); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + (f.P1, _) = (Get1(), 0); + } + + static void Test2(ref T f) where T : struct + { + (f.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + (Program.F.P1, _) = (await Get1Async(), 0); + } + + static async Task Get1Async() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.3 + IL_0025: ldobj ""T"" + IL_002a: ldloc.3 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""T"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(T, int)"" + IL_0013: nop + IL_0014: ret +} +"); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + (f.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + (Program.F.P1, _) = (await Get1Async(), 0); + } + + static async Task Get1Async() + { + System.Console.Write(Program.F.F1); + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 16 (0x10) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""void E.set_P1(ref T, int)"" + IL_000e: nop + IL_000f: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int P1 { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + (default(T).P1, _) = (1, 0); + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,10): error CS1510: A ref or out value must be an assignable variable + // (default(T).P1, _) = (1, 0); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 10), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void PropertyAccess_DeconstructAssignment_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int P1 + { + get + { + throw null; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + (f.P1, _) = (Get1(), 0); + } + + static void Test2(ref T f) where T : class + { + (f.P1, _) = (Get1(), 0); + } + + static int Get1() + { + System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + (Program.F.P1, _) = (await Get1Async(), 0); + } + + static async Task Get1Async() + { + System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123124:123123124:123123124").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 50 (0x32) + .maxstack 2 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.3 + IL_0025: ldobj ""T"" + IL_002a: ldloc.3 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(T, int)"" + IL_0013: nop + IL_0014: ret +} +"); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[0] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[0] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 39 (0x27) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: ldobj ""S1"" + IL_000c: ldc.i4.0 + IL_000d: call ""int E.get_Item(S1, int)"" + IL_0012: call ""int Program.Get1()"" + IL_0017: add + IL_0018: stloc.0 + IL_0019: ldobj ""S1"" + IL_001e: ldc.i4.0 + IL_001f: ldloc.0 + IL_0020: call ""void E.set_Item(S1, int, int)"" + IL_0025: nop + IL_0026: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: ldc.i4.0 + IL_0008: call ""int E.get_Item(S1, int)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldarg.0 + IL_0015: ldobj ""S1"" + IL_001a: ldc.i4.0 + IL_001b: ldloc.0 + IL_001c: call ""void E.set_Item(S1, int, int)"" + IL_0021: nop + IL_0022: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_CompoundAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this[0] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[0] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: ldc.i4.0 + IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 23 (0x17) + .maxstack 4 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldc.i4.0 + IL_0003: ldarg.0 + IL_0004: ldc.i4.0 + IL_0005: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_000a: call ""int Program.Get1()"" + IL_000f: add + IL_0010: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0015: nop + IL_0016: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0] += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_03() + { + var src = """ +static class E +{ + extension(C1 x) + { + public int this[int i] + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[0] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: ldc.i4.0 + IL_000b: call ""int E.get_Item(C1, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(C1, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[0] += Get1(); + } + + static void Test2(ref T f) where T : struct + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 64 (0x40) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: ldc.i4.0 + IL_0026: call ""int E.get_Item(T, int)"" + IL_002b: call ""int Program.Get1()"" + IL_0030: add + IL_0031: stloc.3 + IL_0032: ldobj ""T"" + IL_0037: ldc.i4.0 + IL_0038: ldloc.3 + IL_0039: call ""void E.set_Item(T, int, int)"" + IL_003e: nop + IL_003f: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 35 (0x23) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: ldc.i4.0 + IL_0009: call ""int E.get_Item(T, int)"" + IL_000e: call ""int Program.Get1()"" + IL_0013: add + IL_0014: stloc.0 + IL_0015: ldobj ""T"" + IL_001a: ldc.i4.0 + IL_001b: ldloc.0 + IL_001c: call ""void E.set_Item(T, int, int)"" + IL_0021: nop + IL_0022: ret +} +"); + + var src2 = """ +static class E +{ + extension(T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 25 (0x19) + .maxstack 4 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: ldc.i4.0 + IL_0007: call ""int E.get_Item(ref T, int)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_Item(ref T, int, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + default(T)[0] += 1; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} +"""; + + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_06() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[0] += Get1(); + } + + static void Test2(ref T f) where T : class + { + f[0] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + static async Task Test3() + { + Program.F[0] += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 64 (0x40) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: ldc.i4.0 + IL_0026: call ""int E.get_Item(T, int)"" + IL_002b: call ""int Program.Get1()"" + IL_0030: add + IL_0031: stloc.3 + IL_0032: ldobj ""T"" + IL_0037: ldc.i4.0 + IL_0038: ldloc.3 + IL_0039: call ""void E.set_Item(T, int, int)"" + IL_003e: nop + IL_003f: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 30 (0x1e) + .maxstack 4 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.0 + IL_000a: ldloc.0 + IL_000b: ldc.i4.0 + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: add + IL_0017: call ""void E.set_Item(T, int, int)"" + IL_001c: nop + IL_001d: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_07() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + GetT()[0] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[0] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 73 (0x49) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + T V_3, + int V_4) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.2 + IL_0007: ldloca.s V_2 + IL_0009: stloc.1 + IL_000a: ldloca.s V_3 + IL_000c: initobj ""T"" + IL_0012: ldloc.3 + IL_0013: box ""T"" + IL_0018: brtrue.s IL_0025 + IL_001a: ldloc.1 + IL_001b: ldobj ""T"" + IL_0020: stloc.0 + IL_0021: ldloca.s V_0 + IL_0023: br.s IL_0026 + IL_0025: ldloc.1 + IL_0026: dup + IL_0027: ldobj ""T"" + IL_002c: ldc.i4.0 + IL_002d: call ""int E.get_Item(T, int)"" + IL_0032: call ""int Program.Get1()"" + IL_0037: add + IL_0038: stloc.s V_4 + IL_003a: ldobj ""T"" + IL_003f: ldc.i4.0 + IL_0040: ldloc.s V_4 + IL_0042: call ""void E.set_Item(T, int, int)"" + IL_0047: nop + IL_0048: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_08() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int this[int i] + { + get + { + System.Console.Write(((C1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); + + //await Test3(); + } + + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() + { + GetT()[0] += Get1(); + } + + static void Test2() where T : class + { + GetT()[0] += Get1(); + } + + static int Get1() + { + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[0] += await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 73 (0x49) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + T V_3, + int V_4) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.2 + IL_0007: ldloca.s V_2 + IL_0009: stloc.1 + IL_000a: ldloca.s V_3 + IL_000c: initobj ""T"" + IL_0012: ldloc.3 + IL_0013: box ""T"" + IL_0018: brtrue.s IL_0025 + IL_001a: ldloc.1 + IL_001b: ldobj ""T"" + IL_0020: stloc.0 + IL_0021: ldloca.s V_0 + IL_0023: br.s IL_0026 + IL_0025: ldloc.1 + IL_0026: dup + IL_0027: ldobj ""T"" + IL_002c: ldc.i4.0 + IL_002d: call ""int E.get_Item(T, int)"" + IL_0032: call ""int Program.Get1()"" + IL_0037: add + IL_0038: stloc.s V_4 + IL_003a: ldobj ""T"" + IL_003f: ldc.i4.0 + IL_0040: ldloc.s V_4 + IL_0042: call ""void E.set_Item(T, int, int)"" + IL_0047: nop + IL_0048: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 29 (0x1d) + .maxstack 4 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: ldc.i4.0 + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(T, int, int)"" + IL_001b: nop + IL_001c: ret +} +"); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + this[Program.Get1(), $""] += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Program.Get1(), $""] += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 63 (0x3f) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: ldobj ""S1"" + IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001a: stloc.2 + IL_001b: ldloc.0 + IL_001c: ldobj ""S1"" + IL_0021: ldloc.1 + IL_0022: ldloc.2 + IL_0023: call ""int E.get_Item(S1, int, InterpolationHandler)"" + IL_0028: call ""int Program.Get1()"" + IL_002d: add + IL_002e: stloc.3 + IL_002f: ldloc.0 + IL_0030: ldobj ""S1"" + IL_0035: ldloc.1 + IL_0036: ldloc.2 + IL_0037: ldloc.3 + IL_0038: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_003d: nop + IL_003e: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 59 (0x3b) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""S1"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldobj ""S1"" + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int E.get_Item(S1, int, InterpolationHandler)"" + IL_0024: call ""int Program.Get1()"" + IL_0029: add + IL_002a: stloc.3 + IL_002b: ldloc.0 + IL_002c: ldobj ""S1"" + IL_0031: ldloc.1 + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_0039: nop + IL_003a: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1)[0, $""] += 1; + } +} - verifier.VerifyIL("Program.Test22(ref T)", -@" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - // Code size 62 (0x3e) - .maxstack 3 - .locals init (T& V_0, - int? V_1, - int V_2, - int? V_3) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""int? E.get_P2(T)"" - IL_000e: stloc.1 - IL_000f: ldloca.s V_1 - IL_0011: call ""int int?.GetValueOrDefault()"" - IL_0016: stloc.2 - IL_0017: ldloca.s V_1 - IL_0019: call ""bool int?.HasValue.get"" - IL_001e: brtrue.s IL_003d - IL_0020: call ""int Program.Get1()"" - IL_0025: stloc.2 - IL_0026: ldloc.0 - IL_0027: ldobj ""T"" - IL_002c: ldloca.s V_3 - IL_002e: ldloc.2 - IL_002f: call ""int?..ctor(int)"" - IL_0034: ldloc.3 - IL_0035: call ""void E.set_P2(T, int?)"" - IL_003a: nop - IL_003b: br.s IL_003d - IL_003d: ret + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } -"); +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + ); } - [Fact] - public void PropertyAccess_ConditionalAssignment_05() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_02(string refKind) { - var src = """ -using System.Threading.Tasks; + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} static class E { - extension(ref T x) where T : struct + extension({{{refKind}}} S1 x) { - public object P1 - { - get - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; - } - set - { - System.Console.Write(((S1)(object)x).F1); - } - } - public int? P2 + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - return null; + System.Console.Write(x.F1); + Program.F.F1++; + return 0; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); } } } @@ -8828,237 +12434,295 @@ public int? P2 struct S1 { public int F1; -} -class Program -{ - public static T F; + public void Test() + { + this[Program.Get1(), $""] += Program.Get1(); + } } class Program { - static async Task Main() - { - Program.F = new S1 { F1 = 123 }; - Test12(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - await Test13(); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - Test22(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - await Test23(); - System.Console.Write(Program.F.F1); - } - - static void Test12(ref T f) where T : struct - { - f.P1 ??= Get1(); - } + public static S1 F; - static void Test22(ref T f) where T : struct + static void Main() { - f.P2 ??= Get1(); - } + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); - static int Get1() - { - Program.F.F1++; - return 1; - } + System.Console.Write(":"); - static async Task Test13() where T : struct - { - Program.F.P1 ??= await Get1Async(); + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); } - static async Task Test23() where T : struct + static void Test() { - Program.F.P2 ??= await Get1Async(); + F[Program.Get1(), $""] += Get1(); } - static async Task Get1Async() + public static int Get1() { - Program.F.F1++; - await Task.Yield(); + Program.F.F1++; return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test12(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 31 (0x1f) - .maxstack 3 - .locals init (T& V_0, - object V_1) + // Code size 46 (0x2e) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""object E.get_P1(ref T)"" - IL_0009: brtrue.s IL_001e - IL_000b: ldloc.0 - IL_000c: call ""int Program.Get1()"" - IL_0011: box ""int"" - IL_0016: dup - IL_0017: stloc.1 - IL_0018: call ""void E.set_P1(ref T, object)"" - IL_001d: nop - IL_001e: ret + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret } "); - verifier.VerifyIL("Program.Test22(ref T)", + verifier.VerifyIL("S1.Test", @" { - // Code size 52 (0x34) - .maxstack 3 - .locals init (T& V_0, - int? V_1, - int V_2, - int? V_3) + // Code size 42 (0x2a) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""int? E.get_P2(ref T)"" - IL_0009: stloc.1 - IL_000a: ldloca.s V_1 - IL_000c: call ""int int?.GetValueOrDefault()"" + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" IL_0011: stloc.2 - IL_0012: ldloca.s V_1 - IL_0014: call ""bool int?.HasValue.get"" - IL_0019: brtrue.s IL_0033 - IL_001b: call ""int Program.Get1()"" - IL_0020: stloc.2 - IL_0021: ldloc.0 - IL_0022: ldloca.s V_3 - IL_0024: ldloc.2 - IL_0025: call ""int?..ctor(int)"" - IL_002a: ldloc.3 - IL_002b: call ""void E.set_P2(ref T, int?)"" - IL_0030: nop - IL_0031: br.s IL_0033 - IL_0033: ret + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloc.0 + IL_0016: ldloc.1 + IL_0017: ldloc.2 + IL_0018: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" + IL_001d: call ""int Program.Get1()"" + IL_0022: add + IL_0023: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_0028: nop + IL_0029: ret } "); - var src2 = """ + var src2 = $$$""" static class E { - extension(ref T x) where T : struct + extension({{{refKind}}} S1 x) { - public object P1 { get => 0; set {} } - public int? P2 { get => 0; set {} } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } } +struct S1; + class Program { - static void Test1() where T : struct - { - default(T).P1 += 1; - } - static void Test2() where T : struct + static void Test() { - default(T).P2 += 1; + default(S1)[0, $""] += 1; } } -namespace NS1 +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - static class E - { - extension(in T x) where T : struct - { - } - } -} -namespace NS2 -{ - static class E + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) { - extension(ref readonly T x) where T : struct - { - } } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); comp2.VerifyDiagnostics( - // (14,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P1 += 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), - // (18,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P2 += 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(18, 9), - // (26,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(26, 25), - // (36,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(36, 35) + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) ); } [Fact] - public void PropertyAccess_ConditionalAssignment_06() + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_03() { var src = """ -using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} static class E { - extension(T x) + extension(C1 x) { - public object P1 + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; } set { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(x.F1); } } - public int? P2 + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F[Get1(), $""] += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123127").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 46 (0x2e) + .maxstack 6 + .locals init (C1 V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(C1, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret +} +"); + } + + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return null; + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; } set { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(((S1)(object)x).F1); } } } } -class C1 +struct S1 { public int F1; } @@ -9072,170 +12736,68 @@ class Program { static async Task Main() { - Program.F = new C1 { F1 = 123 }; - Test11(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - Test12(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - await Test13(); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - Test21(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - Test22(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - await Test23(); - System.Console.Write(Program.F.F1); - } - - static void Test11(ref T f) - { - f.P1 ??= Get1(); - } - - static void Test21(ref T f) - { - f.P2 ??= Get1(); - } - - static void Test12(ref T f) where T : class - { - f.P1 ??= Get1(); + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } - static void Test22(ref T f) where T : class + static void Test1(ref T f) { - f.P2 ??= Get1(); + f[Get1(), $""] += Get1(); } - static int Get1() + static void Test2(ref T f) where T : struct { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return 1; + f[Get1(), $""] += Get1(); } - static async Task Test13() + static int Get1() { - Program.F.P1 ??= await Get1Async(); + Program.F.F1++; + return 1; } - static async Task Test23() + static async Task Test3() { - Program.F.P2 ??= await Get1Async(); + Program.F[Get1(), $""] += await Get1Async(); } static async Task Get1Async() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + Program.F.F1++; await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125:123123125:123123125:123123125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test11(ref T)", -@" -{ - // Code size 75 (0x4b) - .maxstack 3 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3, - object V_4, - object V_5) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""object E.get_P1(T)"" - IL_002b: brtrue.s IL_004a - IL_002d: call ""int Program.Get1()"" - IL_0032: box ""int"" - IL_0037: stloc.s V_4 - IL_0039: ldloc.0 - IL_003a: ldobj ""T"" - IL_003f: ldloc.s V_4 - IL_0041: dup - IL_0042: stloc.s V_5 - IL_0044: call ""void E.set_P1(T, object)"" - IL_0049: nop - IL_004a: ret -} -"); - - verifier.VerifyIL("Program.Test12(ref T)", -@" -{ - // Code size 36 (0x24) - .maxstack 3 - .locals init (T V_0, - object V_1) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""object E.get_P1(T)"" - IL_000e: brtrue.s IL_0023 - IL_0010: ldloc.0 - IL_0011: call ""int Program.Get1()"" - IL_0016: box ""int"" - IL_001b: dup - IL_001c: stloc.1 - IL_001d: call ""void E.set_P1(T, object)"" - IL_0022: nop - IL_0023: ret -} -"); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127:124125127127").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test21(ref T)", + verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 96 (0x60) - .maxstack 3 + // Code size 94 (0x5e) + .maxstack 4 .locals init (T& V_0, T V_1, T& V_2, - int? V_3, - int V_4, + int V_3, + InterpolationHandler V_4, T V_5, - int? V_6) + int V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 @@ -9251,191 +12813,169 @@ .locals init (T& V_0, IL_001d: br.s IL_0020 IL_001f: ldloc.2 IL_0020: stloc.0 - IL_0021: ldloc.0 - IL_0022: ldobj ""T"" - IL_0027: call ""int? E.get_P2(T)"" - IL_002c: stloc.3 - IL_002d: ldloca.s V_3 - IL_002f: call ""int int?.GetValueOrDefault()"" + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" IL_0034: stloc.s V_4 - IL_0036: ldloca.s V_3 - IL_0038: call ""bool int?.HasValue.get"" - IL_003d: brtrue.s IL_005f - IL_003f: call ""int Program.Get1()"" - IL_0044: stloc.s V_4 - IL_0046: ldloc.0 - IL_0047: ldobj ""T"" - IL_004c: ldloca.s V_6 - IL_004e: ldloc.s V_4 - IL_0050: call ""int?..ctor(int)"" + IL_0036: ldloc.0 + IL_0037: ldobj ""T"" + IL_003c: ldloc.3 + IL_003d: ldloc.s V_4 + IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0044: call ""int Program.Get1()"" + IL_0049: add + IL_004a: stloc.s V_6 + IL_004c: ldloc.0 + IL_004d: ldobj ""T"" + IL_0052: ldloc.3 + IL_0053: ldloc.s V_4 IL_0055: ldloc.s V_6 - IL_0057: call ""void E.set_P2(T, int?)"" + IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" IL_005c: nop - IL_005d: br.s IL_005f - IL_005f: ret + IL_005d: ret } "); - verifier.VerifyIL("Program.Test22(ref T)", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 57 (0x39) - .maxstack 3 - .locals init (T V_0, - int? V_1, - int V_2, - int? V_3) + // Code size 59 (0x3b) + .maxstack 4 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""int? E.get_P2(T)"" - IL_000e: stloc.1 - IL_000f: ldloca.s V_1 - IL_0011: call ""int int?.GetValueOrDefault()"" + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""T"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" IL_0016: stloc.2 - IL_0017: ldloca.s V_1 - IL_0019: call ""bool int?.HasValue.get"" - IL_001e: brtrue.s IL_0038 - IL_0020: call ""int Program.Get1()"" - IL_0025: stloc.2 - IL_0026: ldloc.0 - IL_0027: ldloca.s V_3 - IL_0029: ldloc.2 - IL_002a: call ""int?..ctor(int)"" - IL_002f: ldloc.3 - IL_0030: call ""void E.set_P2(T, int?)"" - IL_0035: nop - IL_0036: br.s IL_0038 - IL_0038: ret + IL_0017: ldloc.0 + IL_0018: ldobj ""T"" + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0024: call ""int Program.Get1()"" + IL_0029: add + IL_002a: stloc.3 + IL_002b: ldloc.0 + IL_002c: ldobj ""T"" + IL_0031: ldloc.1 + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0039: nop + IL_003a: ret } "); - } - [Fact] - public void PropertyAccess_DeconstructAssignment_01() - { - var src = """ + var src2 = """ static class E { - extension(S1 x) + extension(T x) where T : struct { - public int P1 - { - get - { - throw null; - } - set - { - System.Console.Write(x.F1); - } - } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } } -struct S1 +class Program { - public int F1; - - public void Test() + static void Test() where T : struct { - (this.P1, _) = (Program.Get1(), 0); + default(T)[0, $""] += 1; } } -class Program +namespace NS1 { - public static S1 F; - - static void Main() + static class E { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + extension(in T x) where T : struct + { + } } +} - static void Test() +namespace NS2 +{ + static class E { - (F.P1, _) = (Get1(), 0); + extension(ref readonly T x) where T : struct + { + } } +} - public static int Get1() +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) { - System.Console.Write(Program.F.F1); - Program.F.F1++; - return 1; } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); + } - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 25 (0x19) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: call ""int Program.Get1()"" - IL_000b: stloc.0 - IL_000c: ldobj ""S1"" - IL_0011: ldloc.0 - IL_0012: call ""void E.set_P1(S1, int)"" - IL_0017: nop - IL_0018: ret -} -"); + [Fact] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_05() + { + var src = """ +using System.Threading.Tasks; - verifier.VerifyIL("S1.Test", -@" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - // Code size 21 (0x15) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: call ""int Program.Get1()"" - IL_0006: stloc.0 - IL_0007: ldarg.0 - IL_0008: ldobj ""S1"" - IL_000d: ldloc.0 - IL_000e: call ""void E.set_P1(S1, int)"" - IL_0013: nop - IL_0014: ret -} -"); - } - [Theory] - [InlineData("ref")] - [InlineData("ref readonly")] - [InlineData("in")] - public void PropertyAccess_DeconstructAssignment_02(string refKind) + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) { - var src = $$$""" + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { - extension({{{refKind}}} S1 x) + extension(ref T x) where T : struct { - public int P1 + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - throw null; + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -9444,230 +12984,192 @@ public int P1 struct S1 { public int F1; +} - public void Test() - { - (this.P1, _) = (Program.Get1(), 0); - } +class Program +{ + public static T F; } class Program { - public static S1 F; - - static void Main() + static async Task Main() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } - static void Test() + static void Test2(ref T f) where T : struct { - (F.P1, _) = (Get1(), 0); + f[Get1(), $""] += Get1(); } - public static int Get1() + static int Get1() { - System.Console.Write(Program.F.F1); - Program.F.F1++; + Program.F.F1++; return 1; } -} -"""; - - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 20 (0x14) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: call ""int Program.Get1()"" - IL_000b: stloc.0 - IL_000c: ldloc.0 - IL_000d: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0012: nop - IL_0013: ret -} -"); - - verifier.VerifyIL("S1.Test", -@" -{ - // Code size 16 (0x10) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: call ""int Program.Get1()"" - IL_0006: stloc.0 - IL_0007: ldarg.0 - IL_0008: ldloc.0 - IL_0009: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_000e: nop - IL_000f: ret -} -"); - var src2 = $$$""" -static class E -{ - extension({{{refKind}}} S1 x) + static async Task Test3() where T : struct { - public int P1 { get => 0; set {} } + Program.F[Get1(), $""] += await Get1Async(); } -} - -struct S1; -class Program -{ - static void Test() + static async Task Get1Async() { - (default(S1).P1, _) = (1, 0); + Program.F.F1++; + await Task.Yield(); + return 1; } } """; - var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (15,10): error CS1510: A ref or out value must be an assignable variable - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 10) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (15,10): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 10), - // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (15,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // (default(S1).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 10) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } - } + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); - [Fact] - public void PropertyAccess_DeconstructAssignment_03() - { - var src = """ -static class E + verifier.VerifyIL("Program.Test2(ref T)", +@" { - extension(C1 x) - { - public int P1 - { - get - { - throw null; - } - set - { - System.Console.Write(x.F1); - } - } - } + // Code size 42 (0x2a) + .maxstack 6 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: stloc.2 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloc.0 + IL_0016: ldloc.1 + IL_0017: ldloc.2 + IL_0018: call ""int E.get_Item(ref T, int, InterpolationHandler)"" + IL_001d: call ""int Program.Get1()"" + IL_0022: add + IL_0023: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" + IL_0028: nop + IL_0029: ret } +"); -class C1 + var src2 = """ +static class E { - public int F1; + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + } } class Program { - public static C1 F = new C1 { F1 = 123 }; - - static void Main() + static void Test() where T : struct { - Test(); - System.Console.Write(F.F1); + default(T)[0, $""] += 1; } +} - static void Test() +namespace NS1 +{ + static class E { - (F.P1, _) = (Get1(), 0); + extension(in T x) where T : struct + { + } } +} - static int Get1() +namespace NS2 +{ + static class E { - System.Console.Write(Program.F.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return 1; + extension(ref readonly T x) where T : struct + { + } } } -"""; - - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", -@" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - // Code size 20 (0x14) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: call ""int Program.Get1()"" - IL_000b: stloc.0 - IL_000c: ldloc.0 - IL_000d: call ""void E.set_P1(C1, int)"" - IL_0012: nop - IL_0013: ret + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } -"); +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + comp2.VerifyDiagnostics( + // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); } [Fact] - public void PropertyAccess_DeconstructAssignment_04() + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_06() { var src = """ using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { extension(T x) { - public int P1 + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - throw null; + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(((C1)(object)x).F1); } } } } -struct S1 +class C1 { public int F1; } @@ -9681,125 +13183,175 @@ class Program { static async Task Main() { - Program.F = new S1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - (f.P1, _) = (Get1(), 0); + f[Get1(), $""] += Get1(); } - static void Test2(ref T f) where T : struct + static void Test2(ref T f) where T : class { - (f.P1, _) = (Get1(), 0); + f[Get1(), $""] += Get1(); } static int Get1() { - System.Console.Write(Program.F.F1); - Program.F.F1++; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static async Task Test3() { - (Program.F.P1, _) = (await Get1Async(), 0); + Program.F[Get1(), $""] += await Get1Async(); } static async Task Get1Async() { - System.Console.Write(Program.F.F1); - Program.F.F1++; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124:123124124").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123127:123123123127:123123123127").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 50 (0x32) - .maxstack 2 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) + // Code size 94 (0x5e) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + int V_6) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 + IL_0002: stloc.2 + IL_0003: ldloca.s V_5 IL_0005: initobj ""T"" - IL_000b: ldloc.2 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.3 - IL_0025: ldobj ""T"" - IL_002a: ldloc.3 - IL_002b: call ""void E.set_P1(T, int)"" - IL_0030: nop - IL_0031: ret + IL_000b: ldloc.s V_5 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: ldloc.0 + IL_0037: ldobj ""T"" + IL_003c: ldloc.3 + IL_003d: ldloc.s V_4 + IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0044: call ""int Program.Get1()"" + IL_0049: add + IL_004a: stloc.s V_6 + IL_004c: ldloc.0 + IL_004d: ldobj ""T"" + IL_0052: ldloc.3 + IL_0053: ldloc.s V_4 + IL_0055: ldloc.s V_6 + IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_005c: nop + IL_005d: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 21 (0x15) - .maxstack 2 - .locals init (int V_0) + // Code size 47 (0x2f) + .maxstack 6 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2) IL_0000: nop IL_0001: ldarg.0 - IL_0002: call ""int Program.Get1()"" + IL_0002: ldobj ""T"" IL_0007: stloc.0 - IL_0008: ldobj ""T"" - IL_000d: ldloc.0 - IL_000e: call ""void E.set_P1(T, int)"" - IL_0013: nop - IL_0014: ret + IL_0008: call ""int Program.Get1()"" + IL_000d: stloc.1 + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: ldloc.0 + IL_001b: ldloc.1 + IL_001c: ldloc.2 + IL_001d: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0022: call ""int Program.Get1()"" + IL_0027: add + IL_0028: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002d: nop + IL_002e: ret } "); } [Fact] - public void PropertyAccess_DeconstructAssignment_05() + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_07() { var src = """ using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + static class E { - extension(ref T x) where T : struct + extension(T x) { - public int P1 + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - throw null; + System.Console.Write(((S1)(object)x).F1); + return 0; } set { @@ -9814,140 +13366,136 @@ struct S1 public int F1; } -class Program -{ - public static T F; -} - class Program { +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Test1(); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //await Test3(); } - static void Test2(ref T f) where T : struct + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() { - (f.P1, _) = (Get1(), 0); + GetT()[Get1(), $""] += Get1(); } static int Get1() { - System.Console.Write(Program.F.F1); - Program.F.F1++; return 1; } - static async Task Test3() where T : struct - { - (Program.F.P1, _) = (await Get1Async(), 0); - } + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[Get1(), $""] += await Get1Async(); + //} - static async Task Get1Async() - { - System.Console.Write(Program.F.F1); - Program.F.F1++; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124124:123124124").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 16 (0x10) - .maxstack 2 - .locals init (int V_0) + // Code size 102 (0x66) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + T V_6, + int V_7) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""int Program.Get1()"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""void E.set_P1(ref T, int)"" - IL_000e: nop - IL_000f: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.s V_5 + IL_0008: ldloca.s V_5 + IL_000a: stloc.2 + IL_000b: ldloca.s V_6 + IL_000d: initobj ""T"" + IL_0013: ldloc.s V_6 + IL_0015: box ""T"" + IL_001a: brtrue.s IL_0027 + IL_001c: ldloc.2 + IL_001d: ldobj ""T"" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: br.s IL_0028 + IL_0027: ldloc.2 + IL_0028: stloc.0 + IL_0029: call ""int Program.Get1()"" + IL_002e: stloc.3 + IL_002f: ldc.i4.0 + IL_0030: ldc.i4.0 + IL_0031: ldloc.0 + IL_0032: ldobj ""T"" + IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_003c: stloc.s V_4 + IL_003e: ldloc.0 + IL_003f: ldobj ""T"" + IL_0044: ldloc.3 + IL_0045: ldloc.s V_4 + IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_004c: call ""int Program.Get1()"" + IL_0051: add + IL_0052: stloc.s V_7 + IL_0054: ldloc.0 + IL_0055: ldobj ""T"" + IL_005a: ldloc.3 + IL_005b: ldloc.s V_4 + IL_005d: ldloc.s V_7 + IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0064: nop + IL_0065: ret } "); - - var src2 = """ -static class E -{ - extension(ref T x) where T : struct - { - public int P1 { get => 0; set {} } } -} -class Program -{ - static void Test() where T : struct + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_08() { - (default(T).P1, _) = (1, 0); - } -} + var src = """ +using System.Threading.Tasks; -namespace NS1 +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - static class E - { - extension(in T x) where T : struct - { - } - } -} -namespace NS2 -{ - static class E + public InterpolationHandler(int literalLength, int formattedCount, TR x) { - extension(ref readonly T x) where T : struct - { - } + System.Console.Write(((C1)(object)x).F1); } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } -"""; - - var comp2 = CreateCompilation(src2); - comp2.VerifyDiagnostics( - // (13,10): error CS1510: A ref or out value must be an assignable variable - // (default(T).P1, _) = (1, 0); - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 10), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); - } - - [Fact] - public void PropertyAccess_DeconstructAssignment_06() - { - var src = """ -using System.Threading.Tasks; static class E { extension(T x) { - public int P1 + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - throw null; + System.Console.Write(((C1)(object)x).F1); + return 0; } set { @@ -9962,133 +13510,174 @@ class C1 public int F1; } -class Program -{ - public static T F; -} - class Program { +// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { - Program.F = new C1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); + Test1(); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Test2(); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //await Test3(); } - static void Test1(ref T f) + static T GetT() => (T)(object)new C1 { F1 = 123 }; + + static void Test1() { - (f.P1, _) = (Get1(), 0); + GetT()[Get1(), $""] += Get1(); } - static void Test2(ref T f) where T : class + static void Test2() where T : class { - (f.P1, _) = (Get1(), 0); + GetT()[Get1(), $""] += Get1(); } static int Get1() { - System.Console.Write(Program.F.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } - static async Task Test3() - { - (Program.F.P1, _) = (await Get1Async(), 0); - } + // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed + //static async Task Test3() + //{ + // GetT()[Get1(), $""] += await Get1Async(); + //} - static async Task Get1Async() - { - System.Console.Write(Program.F.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123124:123123124:123123124").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123123:123123123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 50 (0x32) - .maxstack 2 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) + // Code size 102 (0x66) + .maxstack 4 + .locals init (T& V_0, + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + T V_5, + T V_6, + int V_7) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 - IL_0005: initobj ""T"" - IL_000b: ldloc.2 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: call ""int Program.Get1()"" - IL_0024: stloc.3 - IL_0025: ldobj ""T"" - IL_002a: ldloc.3 - IL_002b: call ""void E.set_P1(T, int)"" - IL_0030: nop - IL_0031: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.s V_5 + IL_0008: ldloca.s V_5 + IL_000a: stloc.2 + IL_000b: ldloca.s V_6 + IL_000d: initobj ""T"" + IL_0013: ldloc.s V_6 + IL_0015: box ""T"" + IL_001a: brtrue.s IL_0027 + IL_001c: ldloc.2 + IL_001d: ldobj ""T"" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: br.s IL_0028 + IL_0027: ldloc.2 + IL_0028: stloc.0 + IL_0029: call ""int Program.Get1()"" + IL_002e: stloc.3 + IL_002f: ldc.i4.0 + IL_0030: ldc.i4.0 + IL_0031: ldloc.0 + IL_0032: ldobj ""T"" + IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_003c: stloc.s V_4 + IL_003e: ldloc.0 + IL_003f: ldobj ""T"" + IL_0044: ldloc.3 + IL_0045: ldloc.s V_4 + IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_004c: call ""int Program.Get1()"" + IL_0051: add + IL_0052: stloc.s V_7 + IL_0054: ldloc.0 + IL_0055: ldobj ""T"" + IL_005a: ldloc.3 + IL_005b: ldloc.s V_4 + IL_005d: ldloc.s V_7 + IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0064: nop + IL_0065: ret } "); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 21 (0x15) - .maxstack 2 - .locals init (int V_0) + // Code size 46 (0x2e) + .maxstack 6 + .locals init (T V_0, + int V_1, + InterpolationHandler V_2) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 IL_0007: call ""int Program.Get1()"" - IL_000c: stloc.0 - IL_000d: ldloc.0 - IL_000e: call ""void E.set_P1(T, int)"" - IL_0013: nop - IL_0014: ret + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_0021: call ""int Program.Get1()"" + IL_0026: add + IL_0027: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002c: nop + IL_002d: ret } "); } [Fact] - public void IndexerAccess_CompoundAssignment_01() + public void IndexerAccess_Set_WithInterpolationHandler_01() { var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { extension(S1 x) { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - System.Console.Write(x.F1); - Program.F.F1++; return 0; } set @@ -10099,13 +13688,13 @@ public int this[int i] } } -struct S1 +public struct S1 { public int F1; public void Test() { - this[0] += Program.Get1(); + this[Program.Get1(), $""] = Program.Get1(); } } @@ -10128,7 +13717,7 @@ static void Main() static void Test() { - F[0] += Get1(); + F[Program.Get1(), $""] = Get1(); } public static int Get1() @@ -10139,54 +13728,54 @@ public static int Get1() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 39 (0x27) - .maxstack 3 - .locals init (int V_0) + // Code size 43 (0x2b) + .maxstack 5 + .locals init (S1& V_0) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: ldc.i4.0 - IL_000d: call ""int E.get_Item(S1, int)"" - IL_0012: call ""int Program.Get1()"" - IL_0017: add - IL_0018: stloc.0 - IL_0019: ldobj ""S1"" - IL_001e: ldc.i4.0 - IL_001f: ldloc.0 - IL_0020: call ""void E.set_Item(S1, int, int)"" - IL_0025: nop - IL_0026: ret + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""int Program.Get1()"" + IL_0012: ldc.i4.0 + IL_0013: ldc.i4.0 + IL_0014: ldloc.0 + IL_0015: ldobj ""S1"" + IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001f: call ""int Program.Get1()"" + IL_0024: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_0029: nop + IL_002a: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 35 (0x23) - .maxstack 3 - .locals init (int V_0) + // Code size 39 (0x27) + .maxstack 5 + .locals init (S1& V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: ldc.i4.0 - IL_0008: call ""int E.get_Item(S1, int)"" - IL_000d: call ""int Program.Get1()"" - IL_0012: add - IL_0013: stloc.0 - IL_0014: ldarg.0 - IL_0015: ldobj ""S1"" - IL_001a: ldc.i4.0 - IL_001b: ldloc.0 - IL_001c: call ""void E.set_Item(S1, int, int)"" - IL_0021: nop - IL_0022: ret + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""S1"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""S1"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_0025: nop + IL_0026: ret } "); @@ -10195,7 +13784,7 @@ static class E { extension(S1 x) { - public int this[int i] { get => 0; set {} } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } } @@ -10205,16 +13794,27 @@ class Program { static void Test() { - default(S1)[0] += 1; + default(S1)[0, $""] = 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); comp2.VerifyDiagnostics( // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1)[0] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) ); } @@ -10222,14 +13822,27 @@ static void Test() [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void IndexerAccess_CompoundAssignment_02(string refKind) + public void IndexerAccess_Set_WithInterpolationHandler_02(string refKind) { var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { extension({{{refKind}}} S1 x) { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { @@ -10251,7 +13864,7 @@ struct S1 public void Test() { - this[0] += Program.Get1(); + this[Program.Get1(), $""] = Program.Get1(); } } @@ -10274,7 +13887,7 @@ static void Main() static void Test() { - F[0] += Get1(); + F[Program.Get1(), $""] = Get1(); } public static int Get1() @@ -10285,47 +13898,50 @@ public static int Get1() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 29 (0x1d) - .maxstack 4 + // Code size 33 (0x21) + .maxstack 5 .locals init (S1& V_0) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: ldc.i4.0 - IL_0009: ldloc.0 - IL_000a: ldc.i4.0 - IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" - IL_0010: call ""int Program.Get1()"" - IL_0015: add - IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, int)"" - IL_001b: nop - IL_001c: ret + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 23 (0x17) - .maxstack 4 + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldc.i4.0 - IL_0003: ldarg.0 - IL_0004: ldc.i4.0 - IL_0005: call ""int E.get_Item(" + refKind + @" S1, int)"" - IL_000a: call ""int Program.Get1()"" - IL_000f: add - IL_0010: call ""void E.set_Item(" + refKind + @" S1, int, int)"" - IL_0015: nop - IL_0016: ret + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: nop + IL_001c: ret } "); @@ -10334,7 +13950,7 @@ static class E { extension({{{refKind}}} S1 x) { - public int this[int i] { get => 0; set {} } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } } @@ -10344,28 +13960,52 @@ class Program { static void Test() { - default(S1)[0] += 1; + default(S1)[0, $""] = 1; + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); comp2.VerifyDiagnostics( // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1)[0] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) + // default(S1)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) ); } [Fact] - public void IndexerAccess_CompoundAssignment_03() + public void IndexerAccess_Set_WithInterpolationHandler_03() { var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { extension(C1 x) { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { @@ -10399,7 +14039,7 @@ static void Main() static void Test() { - F[0] += Get1(); + F[Get1(), $""] = Get1(); } static int Get1() @@ -10410,43 +14050,58 @@ static int Get1() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 29 (0x1d) - .maxstack 4 + // Code size 33 (0x21) + .maxstack 5 .locals init (C1 V_0) IL_0000: nop IL_0001: ldsfld ""C1 Program.F"" IL_0006: stloc.0 IL_0007: ldloc.0 - IL_0008: ldc.i4.0 - IL_0009: ldloc.0 - IL_000a: ldc.i4.0 - IL_000b: call ""int E.get_Item(C1, int)"" - IL_0010: call ""int Program.Get1()"" - IL_0015: add - IL_0016: call ""void E.set_Item(C1, int, int)"" - IL_001b: nop - IL_001c: ret + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret } "); } [Fact] - public void IndexerAccess_CompoundAssignment_04() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_WithInterpolationHandler_04() { var src = """ using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + static class E { extension(T x) { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { @@ -10474,6 +14129,8 @@ class Program class Program { +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -10486,21 +14143,22 @@ static async Task Main() Test2(ref Program.F); System.Console.Write(Program.F.F1); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f[0] += Get1(); + f[Get1(), $""] = Get1(); } static void Test2(ref T f) where T : struct { - f[0] += Get1(); + f[Get1(), $""] = Get1(); } static int Get1() @@ -10509,83 +14167,84 @@ static int Get1() return 1; } - static async Task Test3() - { - Program.F[0] += await Get1Async(); - } + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1(), $""] = await Get1Async(); + //} - static async Task Get1Async() - { - Program.F.F1++; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 64 (0x40) - .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) + // Code size 68 (0x44) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 IL_0005: initobj ""T"" - IL_000b: ldloc.2 + IL_000b: ldloc.3 IL_000c: box ""T"" IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 + IL_0013: ldloc.2 IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: ldc.i4.0 - IL_0026: call ""int E.get_Item(T, int)"" - IL_002b: call ""int Program.Get1()"" - IL_0030: add - IL_0031: stloc.3 - IL_0032: ldobj ""T"" - IL_0037: ldc.i4.0 - IL_0038: ldloc.3 - IL_0039: call ""void E.set_Item(T, int, int)"" - IL_003e: nop - IL_003f: ret + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0042: nop + IL_0043: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 35 (0x23) - .maxstack 3 - .locals init (int V_0) + // Code size 39 (0x27) + .maxstack 5 + .locals init (T& V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: ldobj ""T"" - IL_0008: ldc.i4.0 - IL_0009: call ""int E.get_Item(T, int)"" - IL_000e: call ""int Program.Get1()"" - IL_0013: add - IL_0014: stloc.0 - IL_0015: ldobj ""T"" - IL_001a: ldc.i4.0 - IL_001b: ldloc.0 - IL_001c: call ""void E.set_Item(T, int, int)"" - IL_0021: nop - IL_0022: ret + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""T"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0025: nop + IL_0026: ret } "); @@ -10594,7 +14253,7 @@ static class E { extension(T x) where T : struct { - public int this[int i] { get => 0; set {} } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } } @@ -10602,7 +14261,7 @@ class Program { static void Test() where T : struct { - default(T)[0] += 1; + default(T)[0, $""] = 1; } } @@ -10625,13 +14284,24 @@ static class E } } } + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); comp2.VerifyDiagnostics( // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(T)[0] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), @@ -10642,16 +14312,29 @@ static class E } [Fact] - public void IndexerAccess_CompoundAssignment_05() + public void IndexerAccess_Set_WithInterpolationHandler_05() { var src = """ using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { extension(ref T x) where T : struct { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { @@ -10694,7 +14377,7 @@ static async Task Main() static void Test2(ref T f) where T : struct { - f[0] += Get1(); + f[Get1(), $""] = Get1(); } static int Get1() @@ -10705,7 +14388,7 @@ static int Get1() static async Task Test3() where T : struct { - Program.F[0] += await Get1Async(); + Program.F[Get1(), $""] = await Get1Async(); } static async Task Get1Async() @@ -10717,28 +14400,28 @@ static async Task Get1Async() } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 25 (0x19) - .maxstack 4 + // Code size 29 (0x1d) + .maxstack 5 .locals init (T& V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldloc.0 - IL_0004: ldc.i4.0 - IL_0005: ldloc.0 - IL_0006: ldc.i4.0 - IL_0007: call ""int E.get_Item(ref T, int)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: call ""void E.set_Item(ref T, int, int)"" - IL_0017: nop - IL_0018: ret + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" + IL_001b: nop + IL_001c: ret } "); @@ -10747,7 +14430,7 @@ static class E { extension(ref T x) where T : struct { - public int this[int i] { get => 0; set {} } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } } @@ -10755,7 +14438,7 @@ class Program { static void Test() where T : struct { - default(T)[0] += 1; + default(T)[0, $""] = 1; } } @@ -10778,13 +14461,24 @@ static class E } } } + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} """; - var comp2 = CreateCompilation(src2); + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); comp2.VerifyDiagnostics( // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(T)[0] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), + // default(T)[0, $""] += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), @@ -10795,16 +14489,30 @@ static class E } [Fact] - public void IndexerAccess_CompoundAssignment_06() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Set_WithInterpolationHandler_06() { var src = """ using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { extension(T x) { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { @@ -10827,176 +14535,302 @@ class C1 class Program { - public static T F; + public static T F; +} + +class Program +{ +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() + { + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f[Get1(), $""] = Get1(); + } + + static void Test2(ref T f) where T : class + { + f[Get1(), $""] = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // Program.F[Get1(), $""] = await Get1Async(); + //} + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 68 (0x44) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0042: nop + IL_0043: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 34 (0x22) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_0020: nop + IL_0021: ret +} +"); + } + + [Fact] + public void IndexerAccess_Set_WithInterpolationHandler_07() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; } class Program { static async Task Main() { - Program.F = new C1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Test1(); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + await Test3(); } - static void Test1(ref T f) - { - f[0] += Get1(); - } + static T GetT() => (T)(object)new S1 { F1 = 123 }; - static void Test2(ref T f) where T : class + static void Test1() { - f[0] += Get1(); + GetT()[Get1(), $""] = Get1(); } static int Get1() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static async Task Test3() { - Program.F[0] += await Get1Async(); + GetT()[Get1(), $""] = await Get1Async(); } static async Task Get1Async() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test1(ref T)", -@" -{ - // Code size 64 (0x40) - .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - int V_3) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloca.s V_2 - IL_0005: initobj ""T"" - IL_000b: ldloc.2 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.1 - IL_0014: ldobj ""T"" - IL_0019: stloc.0 - IL_001a: ldloca.s V_0 - IL_001c: br.s IL_001f - IL_001e: ldloc.1 - IL_001f: dup - IL_0020: ldobj ""T"" - IL_0025: ldc.i4.0 - IL_0026: call ""int E.get_Item(T, int)"" - IL_002b: call ""int Program.Get1()"" - IL_0030: add - IL_0031: stloc.3 - IL_0032: ldobj ""T"" - IL_0037: ldc.i4.0 - IL_0038: ldloc.3 - IL_0039: call ""void E.set_Item(T, int, int)"" - IL_003e: nop - IL_003f: ret -} -"); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 30 (0x1e) - .maxstack 4 + // Code size 33 (0x21) + .maxstack 5 .locals init (T V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: ldc.i4.0 - IL_000a: ldloc.0 - IL_000b: ldc.i4.0 - IL_000c: call ""int E.get_Item(T, int)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: add - IL_0017: call ""void E.set_Item(T, int, int)"" - IL_001c: nop - IL_001d: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret } "); } [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] - public void IndexerAccess_CompoundAssignment_07() + public void IndexerAccess_Set_WithInterpolationHandler_08() { var src = """ using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((C1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + static class E { extension(T x) { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(((C1)(object)x).F1); return 0; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(((C1)(object)x).F1); } } } } -struct S1 +class C1 { public int F1; } class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { - Test1(); + Test1(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + Test2(); + + System.Console.Write(":"); + + await Test3(); } - static T GetT() => (T)(object)new S1 { F1 = 123 }; + static T GetT() => (T)(object)new C1 { F1 = 123 }; static void Test1() { - GetT()[0] += Get1(); + GetT()[Get1(), $""] = Get1(); + } + + static void Test2() where T : class + { + GetT()[Get1(), $""] = Get1(); } static int Get1() @@ -11004,223 +14838,203 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[0] += await Get1Async(); - //} + static async Task Test3() + { + GetT()[Get1(), $""] = await Get1Async(); + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 73 (0x49) - .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - T V_3, - int V_4) + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.2 - IL_0007: ldloca.s V_2 - IL_0009: stloc.1 - IL_000a: ldloca.s V_3 - IL_000c: initobj ""T"" - IL_0012: ldloc.3 - IL_0013: box ""T"" - IL_0018: brtrue.s IL_0025 - IL_001a: ldloc.1 - IL_001b: ldobj ""T"" - IL_0020: stloc.0 - IL_0021: ldloca.s V_0 - IL_0023: br.s IL_0026 - IL_0025: ldloc.1 - IL_0026: dup - IL_0027: ldobj ""T"" - IL_002c: ldc.i4.0 - IL_002d: call ""int E.get_Item(T, int)"" - IL_0032: call ""int Program.Get1()"" - IL_0037: add - IL_0038: stloc.s V_4 - IL_003a: ldobj ""T"" - IL_003f: ldc.i4.0 - IL_0040: ldloc.s V_4 - IL_0042: call ""void E.set_Item(T, int, int)"" - IL_0047: nop - IL_0048: ret + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_001f: nop + IL_0020: ret } "); } [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] - public void IndexerAccess_CompoundAssignment_08() + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_01() { var src = """ -using System.Threading.Tasks; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} static class E { - extension(T x) + extension(S1 x) { - public int this[int i] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(x.F1); return 0; } - set - { - System.Console.Write(((C1)(object)x).F1); - } } } } -class C1 +public struct S1 { public int F1; + + public void Test() + { + _ = this[Program.Get1(), $"", Program.Get1()]; + } } class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - static async Task Main() + public static S1 F; + + static void Main() { - Test1(); + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); System.Console.Write(":"); - Test2(); - - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); - - //await Test3(); - } - - static T GetT() => (T)(object)new C1 { F1 = 123 }; - - static void Test1() - { - GetT()[0] += Get1(); + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); } - static void Test2() where T : class + static void Test() { - GetT()[0] += Get1(); + _ = F[Program.Get1(), $"", Get1()]; } - static int Get1() + public static int Get1() { + Program.F.F1++; return 1; } - - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[0] += await Get1Async(); - //} - - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1()", + verifier.VerifyIL("Program.Test", @" { - // Code size 73 (0x49) - .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - T V_3, - int V_4) + // Code size 43 (0x2b) + .maxstack 5 + .locals init (S1& V_0) IL_0000: nop - IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.2 - IL_0007: ldloca.s V_2 - IL_0009: stloc.1 - IL_000a: ldloca.s V_3 - IL_000c: initobj ""T"" - IL_0012: ldloc.3 - IL_0013: box ""T"" - IL_0018: brtrue.s IL_0025 - IL_001a: ldloc.1 - IL_001b: ldobj ""T"" - IL_0020: stloc.0 - IL_0021: ldloca.s V_0 - IL_0023: br.s IL_0026 - IL_0025: ldloc.1 - IL_0026: dup - IL_0027: ldobj ""T"" - IL_002c: ldc.i4.0 - IL_002d: call ""int E.get_Item(T, int)"" - IL_0032: call ""int Program.Get1()"" - IL_0037: add - IL_0038: stloc.s V_4 - IL_003a: ldobj ""T"" - IL_003f: ldc.i4.0 - IL_0040: ldloc.s V_4 - IL_0042: call ""void E.set_Item(T, int, int)"" - IL_0047: nop - IL_0048: ret + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldobj ""S1"" + IL_000d: call ""int Program.Get1()"" + IL_0012: ldc.i4.0 + IL_0013: ldc.i4.0 + IL_0014: ldloc.0 + IL_0015: ldobj ""S1"" + IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001f: call ""int Program.Get1()"" + IL_0024: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_0029: pop + IL_002a: ret } "); - verifier.VerifyIL("Program.Test2()", + verifier.VerifyIL("S1.Test", @" { - // Code size 29 (0x1d) - .maxstack 4 - .locals init (T V_0) + // Code size 39 (0x27) + .maxstack 5 + .locals init (S1& V_0) IL_0000: nop - IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldc.i4.0 - IL_0009: ldloc.0 - IL_000a: ldc.i4.0 - IL_000b: call ""int E.get_Item(T, int)"" - IL_0010: call ""int Program.Get1()"" - IL_0015: add - IL_0016: call ""void E.set_Item(T, int, int)"" - IL_001b: nop - IL_001c: ret + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""S1"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""S1"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_0025: pop + IL_0026: ret } "); } - [Fact] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_01() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_02(string refKind) { - var src = """ + var src = $$$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] -public struct InterpolationHandler +struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, S1 x) + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) { System.Console.Write(x.F1); Program.F.F1++; @@ -11231,31 +15045,26 @@ public void AppendLiteral(string value) { } static class E { - extension(S1 x) + extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { System.Console.Write(x.F1); - Program.F.F1++; return 0; } - set - { - System.Console.Write(x.F1); - } } } } -public struct S1 +struct S1 { public int F1; public void Test() { - this[Program.Get1(), $""] += Program.Get1(); + _ = this[Program.Get1(), $"", Program.Get1()]; } } @@ -11278,7 +15087,7 @@ static void Main() static void Test() { - F[Program.Get1(), $""] += Get1(); + _ = F[Program.Get1(), $"", Get1()]; } public static int Get1() @@ -11290,92 +15099,58 @@ public static int Get1() """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 63 (0x3f) - .maxstack 4 - .locals init (S1& V_0, - int V_1, - InterpolationHandler V_2, - int V_3) + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1& V_0) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" IL_0006: stloc.0 - IL_0007: call ""int Program.Get1()"" - IL_000c: stloc.1 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 IL_000f: ldloc.0 - IL_0010: ldobj ""S1"" - IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001a: stloc.2 - IL_001b: ldloc.0 - IL_001c: ldobj ""S1"" - IL_0021: ldloc.1 - IL_0022: ldloc.2 - IL_0023: call ""int E.get_Item(S1, int, InterpolationHandler)"" - IL_0028: call ""int Program.Get1()"" - IL_002d: add - IL_002e: stloc.3 - IL_002f: ldloc.0 - IL_0030: ldobj ""S1"" - IL_0035: ldloc.1 - IL_0036: ldloc.2 - IL_0037: ldloc.3 - IL_0038: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" - IL_003d: nop - IL_003e: ret + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 59 (0x3b) - .maxstack 4 - .locals init (S1& V_0, - int V_1, - InterpolationHandler V_2, - int V_3) + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 - IL_0003: call ""int Program.Get1()"" - IL_0008: stloc.1 - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: ldobj ""S1"" - IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_0016: stloc.2 - IL_0017: ldloc.0 - IL_0018: ldobj ""S1"" - IL_001d: ldloc.1 - IL_001e: ldloc.2 - IL_001f: call ""int E.get_Item(S1, int, InterpolationHandler)"" - IL_0024: call ""int Program.Get1()"" - IL_0029: add - IL_002a: stloc.3 - IL_002b: ldloc.0 - IL_002c: ldobj ""S1"" - IL_0031: ldloc.1 - IL_0032: ldloc.2 - IL_0033: ldloc.3 - IL_0034: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" - IL_0039: nop - IL_003a: ret + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret } "); var src2 = $$$""" static class E { - extension(S1 x) + extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } } } @@ -11385,7 +15160,7 @@ class Program { static void Test() { - default(S1)[0, $""] += 1; + _ = default(S1)[0, $"", 1]; } } @@ -11393,7 +15168,7 @@ static void Test() struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, S1 x) + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) { } public void AppendLiteral(string value) { } @@ -11402,28 +15177,23 @@ public void AppendLiteral(string value) { } """; var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! comp2.VerifyDiagnostics( - // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) ); } - [Theory] - [InlineData("ref")] - [InlineData("ref readonly")] - [InlineData("in")] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_02(string refKind) + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_03() { - var src = $$$""" + var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + public InterpolationHandler(int literalLength, int formattedCount, C1 x) { System.Console.Write(x.F1); - Program.F.F1++; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; @@ -11431,184 +15201,256 @@ public void AppendLiteral(string value) { } static class E { - extension({{{refKind}}} S1 x) + extension(C1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { System.Console.Write(x.F1); - Program.F.F1++; return 0; } - set - { - System.Console.Write(x.F1); - } } } } -struct S1 +class C1 { public int F1; - - public void Test() - { - this[Program.Get1(), $""] += Program.Get1(); - } } class Program { - public static S1 F; + public static C1 F; static void Main() { - F = new S1 { F1 = 123 }; + F = new C1 { F1 = 123 }; Test(); System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); } static void Test() { - F[Program.Get1(), $""] += Get1(); + _ = F[Get1(), $"", Get1()]; } - public static int Get1() + static int Get1() { - Program.F.F1++; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 46 (0x2e) - .maxstack 6 - .locals init (S1& V_0, - int V_1, - InterpolationHandler V_2) + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" + IL_0001: ldsfld ""C1 Program.F"" IL_0006: stloc.0 - IL_0007: call ""int Program.Get1()"" - IL_000c: stloc.1 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" - IL_0015: stloc.2 - IL_0016: ldloc.0 - IL_0017: ldloc.1 - IL_0018: ldloc.2 - IL_0019: ldloc.0 - IL_001a: ldloc.1 - IL_001b: ldloc.2 - IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" - IL_0021: call ""int Program.Get1()"" - IL_0026: add - IL_0027: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" - IL_002c: nop - IL_002d: ret + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret } "); + } - verifier.VerifyIL("S1.Test", -@" + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - // Code size 42 (0x2a) - .maxstack 6 - .locals init (S1& V_0, - int V_1, - InterpolationHandler V_2) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: call ""int Program.Get1()"" - IL_0008: stloc.1 - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" - IL_0011: stloc.2 - IL_0012: ldloc.0 - IL_0013: ldloc.1 - IL_0014: ldloc.2 - IL_0015: ldloc.0 - IL_0016: ldloc.1 - IL_0017: ldloc.2 - IL_0018: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler)"" - IL_001d: call ""int Program.Get1()"" - IL_0022: add - IL_0023: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" - IL_0028: nop - IL_0029: ret + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } -"); - var src2 = $$$""" + static class E { - extension({{{refKind}}} S1 x) + extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } } } -struct S1; +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} class Program { - static void Test() +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + static async Task Main() { - default(S1)[0, $""] += 1; + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); + + //Program.F = new S1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + _ = f[Get1(), $"", Get1()]; + } + + static void Test2(ref T f) where T : struct + { + _ = f[Get1(), $"", Get1()]; + } + + static int Get1() + { + Program.F.F1++; + return 1; } + + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[Get1(), $"", await Get1Async()]; + //} + + //static async Task Get1Async() + //{ + // Program.F.F1++; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 68 (0x44) + .maxstack 5 + .locals init (T& V_0, + T V_1, + T& V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0042: pop + IL_0043: ret } +"); -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler + verifier.VerifyIL("Program.Test2(ref T)", +@" { - - public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) - { - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + // Code size 39 (0x27) + .maxstack 5 + .locals init (T& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldobj ""T"" + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldobj ""T"" + IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_001b: call ""int Program.Get1()"" + IL_0020: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0025: pop + IL_0026: ret } -"""; - - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); - comp2.VerifyDiagnostics( - // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) - ); +"); } [Fact] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_03() + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_05() { var src = """ +using System.Threading.Tasks; + [System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler +struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, C1 x) + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; @@ -11616,92 +15458,159 @@ public void AppendLiteral(string value) { } static class E { - extension(C1 x) + extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + System.Console.Write(((S1)(object)x).F1); return 0; } - set - { - System.Console.Write(x.F1); - } } } } -class C1 +struct S1 { public int F1; } -class Program +class Program { - public static C1 F; + public static T F; +} - static void Main() +class Program +{ + static async Task Main() { - F = new C1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } - static void Test() + static void Test2(ref T f) where T : struct { - F[Get1(), $""] += Get1(); + _ = f[Get1(), $"", Get1()]; } static int Get1() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + _ = Program.F[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); return 1; } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123123127").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", + verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 46 (0x2e) - .maxstack 6 - .locals init (C1 V_0, - int V_1, - InterpolationHandler V_2) + // Code size 29 (0x1d) + .maxstack 5 + .locals init (T& V_0) IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: stloc.0 - IL_0007: call ""int Program.Get1()"" - IL_000c: stloc.1 - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" - IL_0015: stloc.2 - IL_0016: ldloc.0 - IL_0017: ldloc.1 - IL_0018: ldloc.2 - IL_0019: ldloc.0 - IL_001a: ldloc.1 - IL_001b: ldloc.2 - IL_001c: call ""int E.get_Item(C1, int, InterpolationHandler)"" - IL_0021: call ""int Program.Get1()"" - IL_0026: add - IL_0027: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" - IL_002c: nop - IL_002d: ret + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret } "); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } + } +} + +class Program +{ + static void Test() where T : struct + { + _ = default(T)[0, $"", 1]; + } +} + +namespace NS1 +{ + static class E + { + extension(in T x) where T : struct + { + } + } +} + +namespace NS2 +{ + static class E + { + extension(ref readonly T x) where T : struct + { + } + } +} + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + { + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + + // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! + comp2.VerifyDiagnostics( + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); } [Fact] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_04() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_06() { var src = """ using System.Threading.Tasks; @@ -11712,35 +15621,29 @@ struct InterpolationHandler public InterpolationHandler(int literalLength, int formattedCount, TR x) { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; + System.Console.Write(((C1)(object)x).F1); return 0; } - set - { - System.Console.Write(((S1)(object)x).F1); - } } } } -struct S1 +class C1 { public int F1; } @@ -11752,228 +15655,226 @@ class Program class Program { +// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { - Program.F = new S1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); - System.Console.Write(":"); + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + //Program.F = new C1 { F1 = 123 }; + //await Test3(); + //System.Console.Write(Program.F.F1); } static void Test1(ref T f) { - f[Get1(), $""] += Get1(); + _ = f[Get1(), $"", Get1()]; } - static void Test2(ref T f) where T : struct + static void Test2(ref T f) where T : class { - f[Get1(), $""] += Get1(); + _ = f[Get1(), $"", Get1()]; } static int Get1() { - Program.F.F1++; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } - static async Task Test3() - { - Program.F[Get1(), $""] += await Get1Async(); - } + // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed + //static async Task Test3() + //{ + // _ = Program.F[Get1(), $"", await Get1Async()]; + //} - static async Task Get1Async() - { - Program.F.F1++; - await Task.Yield(); - return 1; - } + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127:124125127127").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 94 (0x5e) - .maxstack 4 + // Code size 68 (0x44) + .maxstack 5 .locals init (T& V_0, - T V_1, - T& V_2, - int V_3, - InterpolationHandler V_4, - T V_5, - int V_6) + T V_1, + T& V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 - IL_0003: ldloca.s V_5 + IL_0003: ldloca.s V_3 IL_0005: initobj ""T"" - IL_000b: ldloc.s V_5 - IL_000d: box ""T"" - IL_0012: brtrue.s IL_001f - IL_0014: ldloc.2 - IL_0015: ldobj ""T"" - IL_001a: stloc.1 - IL_001b: ldloca.s V_1 - IL_001d: br.s IL_0020 - IL_001f: ldloc.2 - IL_0020: stloc.0 - IL_0021: call ""int Program.Get1()"" - IL_0026: stloc.3 - IL_0027: ldc.i4.0 - IL_0028: ldc.i4.0 - IL_0029: ldloc.0 - IL_002a: ldobj ""T"" - IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0034: stloc.s V_4 - IL_0036: ldloc.0 - IL_0037: ldobj ""T"" - IL_003c: ldloc.3 - IL_003d: ldloc.s V_4 - IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_0044: call ""int Program.Get1()"" - IL_0049: add - IL_004a: stloc.s V_6 - IL_004c: ldloc.0 - IL_004d: ldobj ""T"" - IL_0052: ldloc.3 - IL_0053: ldloc.s V_4 - IL_0055: ldloc.s V_6 - IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_005c: nop - IL_005d: ret + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.2 + IL_0014: ldobj ""T"" + IL_0019: stloc.1 + IL_001a: ldloca.s V_1 + IL_001c: br.s IL_001f + IL_001e: ldloc.2 + IL_001f: stloc.0 + IL_0020: ldloc.0 + IL_0021: ldobj ""T"" + IL_0026: call ""int Program.Get1()"" + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldloc.0 + IL_002e: ldobj ""T"" + IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0038: call ""int Program.Get1()"" + IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0042: pop + IL_0043: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 59 (0x3b) - .maxstack 4 - .locals init (T& V_0, - int V_1, - InterpolationHandler V_2, - int V_3) + // Code size 34 (0x22) + .maxstack 5 + .locals init (T V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: call ""int Program.Get1()"" - IL_0008: stloc.1 - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: ldobj ""T"" + IL_0002: ldobj ""T"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0016: stloc.2 - IL_0017: ldloc.0 - IL_0018: ldobj ""T"" - IL_001d: ldloc.1 - IL_001e: ldloc.2 - IL_001f: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_0024: call ""int Program.Get1()"" - IL_0029: add - IL_002a: stloc.3 - IL_002b: ldloc.0 - IL_002c: ldobj ""T"" - IL_0031: ldloc.1 - IL_0032: ldloc.2 - IL_0033: ldloc.3 - IL_0034: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0039: nop - IL_003a: ret + IL_0016: call ""int Program.Get1()"" + IL_001b: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_0020: pop + IL_0021: ret } "); - - var src2 = """ -static class E -{ - extension(T x) where T : struct - { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } -} -class Program + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_01() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler { - static void Test() where T : struct + + public InterpolationHandler(int literalLength, int formattedCount, S1 x) { - default(T)[0, $""] += 1; + System.Console.Write(x.F1); } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } -namespace NS1 +static class E { - static class E + extension(S1 x) { - extension(in T x) where T : struct + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { + get + { + System.Console.Write(x.F1); + return 0; + } } } } -namespace NS2 +public struct S1 { - static class E - { - extension(ref readonly T x) where T : struct - { - } - } + public int F1; } -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler +class Program { + static void Main() + { + Test(); + } - public InterpolationHandler(int literalLength, int formattedCount, TR x) + static void Test() { + _ = GetS1()[Program.Get1(), $"", Get1()]; + } + + static S1 GetS1() => new S1 { F1 = 123 }; + + public static int Get1() + { + return 1; } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); - comp2.VerifyDiagnostics( - // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(T)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); - } + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123", verify: Verification.Skipped).VerifyDiagnostics(); - [Fact] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_05() - { - var src = """ -using System.Threading.Tasks; + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (S1 V_0) + IL_0000: nop + IL_0001: call ""S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_02(string refKind) + { + var src = $$$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler +unsafe struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; + System.Console.Write(x.F1); + fixed (int* f1 = &x.F1) + { + (*f1)++; + } } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; @@ -11981,20 +15882,15 @@ public void AppendLiteral(string value) { } static class E { - extension(ref T x) where T : struct + extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; + System.Console.Write(x.F1); return 0; } - set - { - System.Console.Write(((S1)(object)x).F1); - } } } } @@ -12004,152 +15900,138 @@ struct S1 public int F1; } -class Program -{ - public static T F; -} - class Program { - static async Task Main() - { - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); - } - - static void Test2(ref T f) where T : struct + static void Main() { - f[Get1(), $""] += Get1(); + Test(); } - static int Get1() + static void Test() { - Program.F.F1++; - return 1; + _ = GetS1()[Program.Get1(), $"", Get1()]; } - static async Task Test3() where T : struct - { - Program.F[Get1(), $""] += await Get1Async(); - } + static S1 GetS1() => new S1 { F1 = 123 }; - static async Task Get1Async() + public static int Get1() { - Program.F.F1++; - await Task.Yield(); return 1; } } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124125127127:124125127127").VerifyDiagnostics(); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "123124", verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test", @" { - // Code size 42 (0x2a) - .maxstack 6 - .locals init (T& V_0, - int V_1, - InterpolationHandler V_2) + // Code size 35 (0x23) + .maxstack 5 + .locals init (S1 V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: call ""int Program.Get1()"" - IL_0008: stloc.1 - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" - IL_0011: stloc.2 - IL_0012: ldloc.0 - IL_0013: ldloc.1 - IL_0014: ldloc.2 - IL_0015: ldloc.0 - IL_0016: ldloc.1 - IL_0017: ldloc.2 - IL_0018: call ""int E.get_Item(ref T, int, InterpolationHandler)"" - IL_001d: call ""int Program.Get1()"" - IL_0022: add - IL_0023: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" - IL_0028: nop - IL_0029: ret + IL_0001: call ""S1 Program.GetS1()"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloca.s V_0 + IL_0012: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0017: call ""int Program.Get1()"" + IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_0021: pop + IL_0022: ret } "); - - var src2 = """ -static class E -{ - extension(ref T x) where T : struct - { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } } -} -class Program + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_03() + { + var src = """ +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - static void Test() where T : struct + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) { - default(T)[0, $""] += 1; + System.Console.Write(x.F1); } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } -namespace NS1 +static class E { - static class E + extension(C1 x) { - extension(in T x) where T : struct + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { + get + { + System.Console.Write(x.F1); + return 0; + } } } } -namespace NS2 +class C1 { - static class E - { - extension(ref readonly T x) where T : struct - { - } - } + public int F1; } -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler +class Program { + static void Main() + { + Test(); + } - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + static void Test() + { + _ = GetC1()[Get1(), $"", Get1()]; + } + + static C1 GetC1() => new C1 { F1 = 123 }; + + static int Get1() { + return 1; } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); - comp2.VerifyDiagnostics( - // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(T)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: call ""C1 Program.GetC1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); } [Fact] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_06() + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_04() { var src = """ using System.Threading.Tasks; @@ -12160,88 +16042,72 @@ struct InterpolationHandler public InterpolationHandler(int literalLength, int formattedCount, TR x) { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + System.Console.Write(((S1)(object)x).F1); } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } + static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; + System.Console.Write(((S1)(object)x).F1); return 0; } - set - { - System.Console.Write(((C1)(object)x).F1); - } } } } -class C1 +struct S1 { public int F1; } -class Program -{ - public static T F; -} - class Program { static async Task Main() { - Program.F = new C1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); + Test1(); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Test2(); System.Console.Write(":"); - Program.F = new C1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + await Test3(); } - static void Test1(ref T f) + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() { - f[Get1(), $""] += Get1(); + _ = GetT()[Get1(), $"", Get1()]; } - static void Test2(ref T f) where T : class + static void Test2() where T : struct { - f[Get1(), $""] += Get1(); + _ = GetT()[Get1(), $"", Get1()]; } static int Get1() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static async Task Test3() { - Program.F[Get1(), $""] += await Get1Async(); + _ = GetT()[Get1(), $"", await Get1Async()]; } static async Task Get1Async() { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; await Task.Yield(); return 1; } @@ -12249,100 +16115,55 @@ static async Task Get1Async() """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123123127:123123123127:123123123127").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 94 (0x5e) - .maxstack 4 - .locals init (T& V_0, - T V_1, - T& V_2, - int V_3, - InterpolationHandler V_4, - T V_5, - int V_6) + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_5 - IL_0005: initobj ""T"" - IL_000b: ldloc.s V_5 - IL_000d: box ""T"" - IL_0012: brtrue.s IL_001f - IL_0014: ldloc.2 - IL_0015: ldobj ""T"" - IL_001a: stloc.1 - IL_001b: ldloca.s V_1 - IL_001d: br.s IL_0020 - IL_001f: ldloc.2 - IL_0020: stloc.0 - IL_0021: call ""int Program.Get1()"" - IL_0026: stloc.3 - IL_0027: ldc.i4.0 - IL_0028: ldc.i4.0 - IL_0029: ldloc.0 - IL_002a: ldobj ""T"" - IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0034: stloc.s V_4 - IL_0036: ldloc.0 - IL_0037: ldobj ""T"" - IL_003c: ldloc.3 - IL_003d: ldloc.s V_4 - IL_003f: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_0044: call ""int Program.Get1()"" - IL_0049: add - IL_004a: stloc.s V_6 - IL_004c: ldloc.0 - IL_004d: ldobj ""T"" - IL_0052: ldloc.3 - IL_0053: ldloc.s V_4 - IL_0055: ldloc.s V_6 - IL_0057: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_005c: nop - IL_005d: ret + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret } "); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 47 (0x2f) - .maxstack 6 - .locals init (T V_0, - int V_1, - InterpolationHandler V_2) + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: stloc.0 + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 IL_0008: call ""int Program.Get1()"" - IL_000d: stloc.1 + IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0016: stloc.2 - IL_0017: ldloc.0 - IL_0018: ldloc.1 - IL_0019: ldloc.2 - IL_001a: ldloc.0 - IL_001b: ldloc.1 - IL_001c: ldloc.2 - IL_001d: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_0022: call ""int Program.Get1()"" - IL_0027: add - IL_0028: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_002d: nop - IL_002e: ret + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret } "); } [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_07() + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_05() { var src = """ using System.Threading.Tasks; @@ -12351,30 +16172,26 @@ public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_07() struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, TR x) + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) { System.Console.Write(((S1)(object)x).F1); + x = (TR)(object)new S1 { F1 = ((S1)(object)x).F1 + 1 }; } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } - static class E { - extension(T x) + extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { System.Console.Write(((S1)(object)x).F1); return 0; } - set - { - System.Console.Write(((S1)(object)x).F1); - } } } } @@ -12386,108 +16203,68 @@ struct S1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { - Test1(); + Test2(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } - static T GetT() => (T)(object)new S1 { F1 = 123 }; - - static void Test1() + static void Test2() where T : struct { - GetT()[Get1(), $""] += Get1(); + _ = GetT()[Get1(), $"", Get1()]; } + static T GetT() => (T)(object)new S1 { F1 = 123 }; + static int Get1() { return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[Get1(), $""] += await Get1Async(); - //} + static async Task Test3() where T : struct + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123123").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1()", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 102 (0x66) - .maxstack 4 - .locals init (T& V_0, - T V_1, - T& V_2, - int V_3, - InterpolationHandler V_4, - T V_5, - T V_6, - int V_7) + // Code size 35 (0x23) + .maxstack 5 + .locals init (T V_0) IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.s V_5 - IL_0008: ldloca.s V_5 - IL_000a: stloc.2 - IL_000b: ldloca.s V_6 - IL_000d: initobj ""T"" - IL_0013: ldloc.s V_6 - IL_0015: box ""T"" - IL_001a: brtrue.s IL_0027 - IL_001c: ldloc.2 - IL_001d: ldobj ""T"" - IL_0022: stloc.1 - IL_0023: ldloca.s V_1 - IL_0025: br.s IL_0028 - IL_0027: ldloc.2 - IL_0028: stloc.0 - IL_0029: call ""int Program.Get1()"" - IL_002e: stloc.3 - IL_002f: ldc.i4.0 - IL_0030: ldc.i4.0 - IL_0031: ldloc.0 - IL_0032: ldobj ""T"" - IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_003c: stloc.s V_4 - IL_003e: ldloc.0 - IL_003f: ldobj ""T"" - IL_0044: ldloc.3 - IL_0045: ldloc.s V_4 - IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_004c: call ""int Program.Get1()"" - IL_0051: add - IL_0052: stloc.s V_7 - IL_0054: ldloc.0 - IL_0055: ldobj ""T"" - IL_005a: ldloc.3 - IL_005b: ldloc.s V_4 - IL_005d: ldloc.s V_7 - IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0064: nop - IL_0065: ret + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.0 + IL_0010: ldloca.s V_0 + IL_0012: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0017: call ""int Program.Get1()"" + IL_001c: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" + IL_0021: pop + IL_0022: ret } "); } [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] - public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_08() + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_06() { var src = """ using System.Threading.Tasks; @@ -12508,17 +16285,13 @@ static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { System.Console.Write(((C1)(object)x).F1); return 0; } - set - { - System.Console.Write(((C1)(object)x).F1); - } } } } @@ -12530,8 +16303,6 @@ class C1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Test1(); @@ -12540,22 +16311,21 @@ static async Task Main() Test2(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } static T GetT() => (T)(object)new C1 { F1 = 123 }; static void Test1() { - GetT()[Get1(), $""] += Get1(); + _ = GetT()[Get1(), $"", Get1()]; } static void Test2() where T : class { - GetT()[Get1(), $""] += Get1(); + _ = GetT()[Get1(), $"", Get1()]; } static int Get1() @@ -12563,136 +16333,76 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[Get1(), $""] += await Get1Async(); - //} - - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Test3() + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123123:123123123").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 102 (0x66) - .maxstack 4 - .locals init (T& V_0, - T V_1, - T& V_2, - int V_3, - InterpolationHandler V_4, - T V_5, - T V_6, - int V_7) + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.s V_5 - IL_0008: ldloca.s V_5 - IL_000a: stloc.2 - IL_000b: ldloca.s V_6 - IL_000d: initobj ""T"" - IL_0013: ldloc.s V_6 - IL_0015: box ""T"" - IL_001a: brtrue.s IL_0027 - IL_001c: ldloc.2 - IL_001d: ldobj ""T"" - IL_0022: stloc.1 - IL_0023: ldloca.s V_1 - IL_0025: br.s IL_0028 - IL_0027: ldloc.2 - IL_0028: stloc.0 - IL_0029: call ""int Program.Get1()"" - IL_002e: stloc.3 - IL_002f: ldc.i4.0 - IL_0030: ldc.i4.0 - IL_0031: ldloc.0 - IL_0032: ldobj ""T"" - IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_003c: stloc.s V_4 - IL_003e: ldloc.0 - IL_003f: ldobj ""T"" - IL_0044: ldloc.3 - IL_0045: ldloc.s V_4 - IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_004c: call ""int Program.Get1()"" - IL_0051: add - IL_0052: stloc.s V_7 - IL_0054: ldloc.0 - IL_0055: ldobj ""T"" - IL_005a: ldloc.3 - IL_005b: ldloc.s V_4 - IL_005d: ldloc.s V_7 - IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0064: nop - IL_0065: ret + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret } "); verifier.VerifyIL("Program.Test2()", @" { - // Code size 46 (0x2e) - .maxstack 6 - .locals init (T V_0, - int V_1, - InterpolationHandler V_2) + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) IL_0000: nop IL_0001: call ""T Program.GetT()"" IL_0006: stloc.0 - IL_0007: call ""int Program.Get1()"" - IL_000c: stloc.1 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 IL_000f: ldloc.0 IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: stloc.2 - IL_0016: ldloc.0 - IL_0017: ldloc.1 - IL_0018: ldloc.2 - IL_0019: ldloc.0 - IL_001a: ldloc.1 - IL_001b: ldloc.2 - IL_001c: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_0021: call ""int Program.Get1()"" - IL_0026: add - IL_0027: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_002c: nop - IL_002d: ret + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret } "); } [Fact] - public void IndexerAccess_Set_WithInterpolationHandler_01() + public void IndexerAccess_Set_01() { var src = """ -[System.Runtime.CompilerServices.InterpolatedStringHandler] -public struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, S1 x) - { - System.Console.Write(x.F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -12712,7 +16422,7 @@ public struct S1 public void Test() { - this[Program.Get1(), $""] = Program.Get1(); + this[Program.Get1()] = Program.Get1(); } } @@ -12735,7 +16445,7 @@ static void Main() static void Test() { - F[Program.Get1(), $""] = Get1(); + F[Program.Get1()] = Get1(); } public static int Get1() @@ -12746,54 +16456,37 @@ public static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125:123125", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 43 (0x2b) - .maxstack 5 - .locals init (S1& V_0) + // Code size 23 (0x17) + .maxstack 3 IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""int Program.Get1()"" - IL_0012: ldc.i4.0 - IL_0013: ldc.i4.0 - IL_0014: ldloc.0 - IL_0015: ldobj ""S1"" - IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001f: call ""int Program.Get1()"" - IL_0024: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" - IL_0029: nop - IL_002a: ret + IL_0001: ldsfld ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(S1, int, int)"" + IL_0015: nop + IL_0016: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (S1& V_0) + // Code size 24 (0x18) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""S1"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""S1"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" - IL_0025: nop - IL_0026: ret + IL_0002: ldobj ""S1"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int Program.Get1()"" + IL_0011: call ""void E.set_Item(S1, int, int)"" + IL_0016: nop + IL_0017: ret } "); @@ -12802,7 +16495,7 @@ static class E { extension(S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + public int this[int i] { get => 0; set {} } } } @@ -12812,27 +16505,16 @@ class Program { static void Test() { - default(S1)[0, $""] = 1; - } -} - -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, S1 x) - { + default(S1)[0] = 1; } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + var comp2 = CreateCompilation([src2]); comp2.VerifyDiagnostics( // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + // default(S1)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) ); } @@ -12840,27 +16522,14 @@ public void AppendLiteral(string value) { } [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void IndexerAccess_Set_WithInterpolationHandler_02(string refKind) + public void IndexerAccess_Set_02(string refKind) { var src = $$$""" -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) - { - System.Console.Write(x.F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -12882,7 +16551,7 @@ struct S1 public void Test() { - this[Program.Get1(), $""] = Program.Get1(); + this[Program.Get1()] = Program.Get1(); } } @@ -12905,7 +16574,7 @@ static void Main() static void Test() { - F[Program.Get1(), $""] = Get1(); + F[Program.Get1()] = Get1(); } public static int Get1() @@ -12916,50 +16585,36 @@ public static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (S1& V_0) + // Code size 23 (0x17) + .maxstack 3 IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" - IL_001f: nop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0015: nop + IL_0016: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 29 (0x1d) - .maxstack 5 - .locals init (S1& V_0) + // Code size 19 (0x13) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""int Program.Get1()"" - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" - IL_001b: nop - IL_001c: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0011: nop + IL_0012: ret } "); @@ -12968,7 +16623,7 @@ static class E { extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + public int this[int i] { get => 0; set {} } } } @@ -12978,52 +16633,28 @@ class Program { static void Test() { - default(S1)[0, $""] = 1; - } -} - -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) - { + default(S1)[0] = 1; } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + var comp2 = CreateCompilation([src2]); comp2.VerifyDiagnostics( // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(S1)[0, $""""]").WithLocation(15, 9) + // default(S1)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[0]").WithLocation(15, 9) ); } [Fact] - public void IndexerAccess_Set_WithInterpolationHandler_03() + public void IndexerAccess_Set_03() { var src = """ -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, C1 x) - { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(C1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -13057,7 +16688,7 @@ static void Main() static void Test() { - F[Get1(), $""] = Get1(); + F[Get1()] = Get1(); } static int Get1() @@ -13068,58 +16699,37 @@ static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (C1 V_0) + // Code size 23 (0x17) + .maxstack 3 IL_0000: nop IL_0001: ldsfld ""C1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""void E.set_Item(C1, int, InterpolationHandler, int)"" - IL_001f: nop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(C1, int, int)"" + IL_0015: nop + IL_0016: ret } "); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] - public void IndexerAccess_Set_WithInterpolationHandler_04() + public void IndexerAccess_Set_04() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -13171,12 +16781,12 @@ static async Task Main() static void Test1(ref T f) { - f[Get1(), $""] = Get1(); + f[Get1()] = Get1(); } static void Test2(ref T f) where T : struct { - f[Get1(), $""] = Get1(); + f[Get1()] = Get1(); } static int Get1() @@ -13188,7 +16798,7 @@ static int Get1() // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3() //{ - // Program.F[Get1(), $""] = await Get1Async(); + // Program.F[Get1()] = await Get1Async(); //} //static async Task Get1Async() @@ -13200,69 +16810,38 @@ static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125:123125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 68 (0x44) - .maxstack 5 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) + // Code size 24 (0x18) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0042: nop - IL_0043: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int Program.Get1()"" + IL_0011: call ""void E.set_Item(T, int, int)"" + IL_0016: nop + IL_0017: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (T& V_0) + // Code size 24 (0x18) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""T"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0025: nop - IL_0026: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int Program.Get1()"" + IL_0011: call ""void E.set_Item(T, int, int)"" + IL_0016: nop + IL_0017: ret } "); @@ -13271,7 +16850,7 @@ static class E { extension(T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + public int this[int i] { get => 0; set {} } } } @@ -13279,7 +16858,7 @@ class Program { static void Test() where T : struct { - default(T)[0, $""] = 1; + default(T)[0] = 1; } } @@ -13302,24 +16881,13 @@ static class E } } } - -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + var comp2 = CreateCompilation([src2]); comp2.VerifyDiagnostics( // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(T)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // default(T)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), @@ -13330,29 +16898,16 @@ public void AppendLiteral(string value) { } } [Fact] - public void IndexerAccess_Set_WithInterpolationHandler_05() + public void IndexerAccess_Set_05() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -13395,7 +16950,7 @@ static async Task Main() static void Test2(ref T f) where T : struct { - f[Get1(), $""] = Get1(); + f[Get1()] = Get1(); } static int Get1() @@ -13406,7 +16961,7 @@ static int Get1() static async Task Test3() where T : struct { - Program.F[Get1(), $""] = await Get1Async(); + Program.F[Get1()] = await Get1Async(); } static async Task Get1Async() @@ -13418,28 +16973,21 @@ static async Task Get1Async() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 29 (0x1d) - .maxstack 5 - .locals init (T& V_0) + // Code size 19 (0x13) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""int Program.Get1()"" - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: call ""void E.set_Item(ref T, int, InterpolationHandler, int)"" - IL_001b: nop - IL_001c: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""void E.set_Item(ref T, int, int)"" + IL_0011: nop + IL_0012: ret } "); @@ -13448,7 +16996,7 @@ static class E { extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] { get => 0; set {} } + public int this[int i] { get => 0; set {} } } } @@ -13456,7 +17004,7 @@ class Program { static void Test() where T : struct { - default(T)[0, $""] = 1; + default(T)[0] = 1; } } @@ -13479,24 +17027,13 @@ static class E } } } - -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) - { - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + var comp2 = CreateCompilation([src2]); comp2.VerifyDiagnostics( // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(T)[0, $""] += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, @"default(T)[0, $""""]").WithLocation(13, 9), + // default(T)[0] = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[0]").WithLocation(13, 9), // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), @@ -13508,29 +17045,16 @@ public void AppendLiteral(string value) { } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] - public void IndexerAccess_Set_WithInterpolationHandler_06() + public void IndexerAccess_Set_06() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -13582,12 +17106,12 @@ static async Task Main() static void Test1(ref T f) { - f[Get1(), $""] = Get1(); + f[Get1()] = Get1(); } static void Test2(ref T f) where T : class { - f[Get1(), $""] = Get1(); + f[Get1()] = Get1(); } static int Get1() @@ -13599,108 +17123,65 @@ static int Get1() // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3() //{ - // Program.F[Get1(), $""] = await Get1Async(); - //} - - //static async Task Get1Async() - //{ - // Program.F = new C1 { F1 = Program.F.F1 + 1 }; - // await Task.Yield(); - // return 1; + // Program.F[Get1()] = await Get1Async(); //} -} -"""; - - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test1(ref T)", -@" -{ - // Code size 68 (0x44) - .maxstack 5 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0042: nop - IL_0043: ret + + //static async Task Get1Async() + //{ + // Program.F = new C1 { F1 = Program.F.F1 + 1 }; + // await Task.Yield(); + // return 1; + //} +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125:123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 24 (0x18) + .maxstack 3 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int Program.Get1()"" + IL_0011: call ""void E.set_Item(T, int, int)"" + IL_0016: nop + IL_0017: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 34 (0x22) - .maxstack 5 - .locals init (T V_0) + // Code size 24 (0x18) + .maxstack 3 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj ""T"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0016: call ""int Program.Get1()"" - IL_001b: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0020: nop - IL_0021: ret + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int Program.Get1()"" + IL_0011: call ""void E.set_Item(T, int, int)"" + IL_0016: nop + IL_0017: ret } "); } [Fact] - public void IndexerAccess_Set_WithInterpolationHandler_07() + public void IndexerAccess_Set_07() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((S1)(object)x).F1); - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -13735,7 +17216,7 @@ static async Task Main() static void Test1() { - GetT()[Get1(), $""] = Get1(); + GetT()[Get1()] = Get1(); } static int Get1() @@ -13745,7 +17226,7 @@ static int Get1() static async Task Test3() { - GetT()[Get1(), $""] = await Get1Async(); + GetT()[Get1()] = await Get1Async(); } static async Task Get1Async() @@ -13756,55 +17237,36 @@ static async Task Get1Async() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) + // Code size 23 (0x17) + .maxstack 3 IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_001f: nop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(T, int, int)"" + IL_0015: nop + IL_0016: ret } "); } [Fact] - public void IndexerAccess_Set_WithInterpolationHandler_08() + public void IndexerAccess_Set_08() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((C1)(object)x).F1); - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h] + public int this[int i] { get { @@ -13843,12 +17305,12 @@ static async Task Main() static void Test1() { - GetT()[Get1(), $""] = Get1(); + GetT()[Get1()] = Get1(); } static void Test2() where T : class { - GetT()[Get1(), $""] = Get1(); + GetT()[Get1()] = Get1(); } static int Get1() @@ -13858,7 +17320,7 @@ static int Get1() static async Task Test3() { - GetT()[Get1(), $""] = await Get1Async(); + GetT()[Get1()] = await Get1Async(); } static async Task Get1Async() @@ -13869,76 +17331,49 @@ static async Task Get1Async() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) + // Code size 23 (0x17) + .maxstack 3 IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_001f: nop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(T, int, int)"" + IL_0015: nop + IL_0016: ret } "); verifier.VerifyIL("Program.Test2()", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) + // Code size 23 (0x17) + .maxstack 3 IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_001f: nop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int Program.Get1()"" + IL_0010: call ""void E.set_Item(T, int, int)"" + IL_0015: nop + IL_0016: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_01() + public void IndexerAccess_Get_LValueReceiver_01() { var src = """ -[System.Runtime.CompilerServices.InterpolatedStringHandler] -public struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, S1 x) - { - System.Console.Write(x.F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -13955,7 +17390,7 @@ public struct S1 public void Test() { - _ = this[Program.Get1(), $"", Program.Get1()]; + _ = this[Program.Get1()]; } } @@ -13978,7 +17413,7 @@ static void Main() static void Test() { - _ = F[Program.Get1(), $"", Get1()]; + _ = F[Program.Get1()]; } public static int Get1() @@ -13989,54 +17424,35 @@ public static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 43 (0x2b) - .maxstack 5 - .locals init (S1& V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""int Program.Get1()"" - IL_0012: ldc.i4.0 - IL_0013: ldc.i4.0 - IL_0014: ldloc.0 - IL_0015: ldobj ""S1"" - IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001f: call ""int Program.Get1()"" - IL_0024: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" - IL_0029: pop - IL_002a: ret + IL_0001: ldsfld ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(S1, int)"" + IL_0010: pop + IL_0011: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (S1& V_0) + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""S1"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""S1"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" - IL_0025: pop - IL_0026: ret + IL_0002: ldobj ""S1"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int E.get_Item(S1, int)"" + IL_0011: pop + IL_0012: ret } "); } @@ -14045,27 +17461,14 @@ .locals init (S1& V_0) [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_02(string refKind) + public void IndexerAccess_Get_LValueReceiver_02(string refKind) { var src = $$$""" -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) - { - System.Console.Write(x.F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -14082,7 +17485,7 @@ struct S1 public void Test() { - _ = this[Program.Get1(), $"", Program.Get1()]; + _ = this[Program.Get1()]; } } @@ -14105,7 +17508,7 @@ static void Main() static void Test() { - _ = F[Program.Get1(), $"", Get1()]; + _ = F[Program.Get1()]; } public static int Get1() @@ -14116,50 +17519,34 @@ public static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (S1& V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0010: pop + IL_0011: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 29 (0x1d) - .maxstack 5 - .locals init (S1& V_0) + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""int Program.Get1()"" - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" - IL_001b: pop - IL_001c: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_000c: pop + IL_000d: ret } "); @@ -14168,60 +17555,36 @@ static class E { extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } - } -} - -struct S1; - -class Program -{ - static void Test() - { - _ = default(S1)[0, $"", 1]; + public int this[int i] { get => 0; set {} } } } -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ +struct S1; - public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) +class Program +{ + static void Test() { + _ = default(S1)[0]; } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + var comp2 = CreateCompilation([src2]); // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! comp2.VerifyDiagnostics( ); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_03() + public void IndexerAccess_Get_LValueReceiver_03() { var src = """ -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, C1 x) - { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(C1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -14250,7 +17613,7 @@ static void Main() static void Test() { - _ = F[Get1(), $"", Get1()]; + _ = F[Get1()]; } static int Get1() @@ -14261,58 +17624,36 @@ static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (C1 V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: ldsfld ""C1 Program.F"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(C1, int)"" + IL_0010: pop + IL_0011: ret } "); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] - public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_04() + public void IndexerAccess_Get_LValueReceiver_04() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -14359,12 +17700,12 @@ static async Task Main() static void Test1(ref T f) { - _ = f[Get1(), $"", Get1()]; + _ = f[Get1()]; } static void Test2(ref T f) where T : struct { - _ = f[Get1(), $"", Get1()]; + _ = f[Get1()]; } static int Get1() @@ -14376,7 +17717,7 @@ static int Get1() // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3() //{ - // _ = Program.F[Get1(), $"", await Get1Async()]; + // _ = Program.F[await Get1Async()]; //} //static async Task Get1Async() @@ -14388,97 +17729,51 @@ static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 68 (0x44) - .maxstack 5 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_0042: pop - IL_0043: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: pop + IL_0012: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (T& V_0) + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""T"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_0025: pop - IL_0026: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: pop + IL_0012: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_05() + public void IndexerAccess_Get_LValueReceiver_05() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) - { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -14516,7 +17811,7 @@ static async Task Main() static void Test2(ref T f) where T : struct { - _ = f[Get1(), $"", Get1()]; + _ = f[Get1()]; } static int Get1() @@ -14527,7 +17822,7 @@ static int Get1() static async Task Test3() where T : struct { - _ = Program.F[Get1(), $"", await Get1Async()]; + _ = Program.F[await Get1Async()]; } static async Task Get1Async() @@ -14539,28 +17834,20 @@ static async Task Get1Async() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 29 (0x1d) - .maxstack 5 - .locals init (T& V_0) + // Code size 14 (0xe) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: call ""int Program.Get1()"" - IL_0009: ldc.i4.0 - IL_000a: ldc.i4.0 - IL_000b: ldloc.0 - IL_000c: newobj ""InterpolationHandler..ctor(int, int, ref T)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" - IL_001b: pop - IL_001c: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: call ""int E.get_Item(ref T, int)"" + IL_000c: pop + IL_000d: ret } "); @@ -14569,7 +17856,7 @@ static class E { extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get => 0; set {} } + public int this[int i] { get => 0; set {} } } } @@ -14577,7 +17864,7 @@ class Program { static void Test() where T : struct { - _ = default(T)[0, $"", 1]; + _ = default(T)[0]; } } @@ -14600,20 +17887,9 @@ static class E } } } - -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) - { - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} """; - var comp2 = CreateCompilation([src2, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); + var comp2 = CreateCompilation([src2]); // !!! Shouldn't there be a not a variable error for 'default(T)[0, $"", 1]' !!! comp2.VerifyDiagnostics( @@ -14628,29 +17904,16 @@ public void AppendLiteral(string value) { } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] - public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_06() + public void IndexerAccess_Get_LValueReceiver_06() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((C1)(object)x).F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -14697,12 +17960,12 @@ static async Task Main() static void Test1(ref T f) { - _ = f[Get1(), $"", Get1()]; + _ = f[Get1()]; } static void Test2(ref T f) where T : class { - _ = f[Get1(), $"", Get1()]; + _ = f[Get1()]; } static int Get1() @@ -14714,7 +17977,7 @@ static int Get1() // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3() //{ - // _ = Program.F[Get1(), $"", await Get1Async()]; + // _ = Program.F[await Get1Async()]; //} //static async Task Get1Async() @@ -14726,93 +17989,49 @@ static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123126:123123126").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 68 (0x44) - .maxstack 5 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) + // Code size 19 (0x13) + .maxstack 2 IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.2 - IL_0003: ldloca.s V_3 - IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_0042: pop - IL_0043: ret + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: pop + IL_0012: ret } "); - - verifier.VerifyIL("Program.Test2(ref T)", -@" -{ - // Code size 34 (0x22) - .maxstack 5 - .locals init (T V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: stloc.0 - IL_0008: ldloc.0 - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0016: call ""int Program.Get1()"" - IL_001b: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_0020: pop - IL_0021: ret + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 19 (0x13) + .maxstack 2 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""T"" + IL_0007: call ""int Program.Get1()"" + IL_000c: call ""int E.get_Item(T, int)"" + IL_0011: pop + IL_0012: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_01() + public void IndexerAccess_Get_RValueReceiver_01() { var src = """ -[System.Runtime.CompilerServices.InterpolatedStringHandler] -public struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, S1 x) - { - System.Console.Write(x.F1); - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -14837,7 +18056,7 @@ static void Main() static void Test() { - _ = GetS1()[Program.Get1(), $"", Get1()]; + _ = GetS1()[Get1()]; } static S1 GetS1() => new S1 { F1 = 123 }; @@ -14849,28 +18068,20 @@ public static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123", verify: Verification.Skipped).VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (S1 V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: call ""S1 Program.GetS1()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(S1, int)"" + IL_0010: pop + IL_0011: ret } "); } @@ -14879,30 +18090,14 @@ .locals init (S1 V_0) [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_02(string refKind) + public void IndexerAccess_Get_RValueReceiver_02(string refKind) { var src = $$$""" -[System.Runtime.CompilerServices.InterpolatedStringHandler] -unsafe struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) - { - System.Console.Write(x.F1); - fixed (int* f1 = &x.F1) - { - (*f1)++; - } - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -14927,7 +18122,7 @@ static void Main() static void Test() { - _ = GetS1()[Program.Get1(), $"", Get1()]; + _ = GetS1()[Get1()]; } static S1 GetS1() => new S1 { F1 = 123 }; @@ -14939,53 +18134,36 @@ public static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe.WithAllowUnsafe(true)); - var verifier = CompileAndVerify(comp, expectedOutput: "123124", verify: Verification.Skipped).VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 35 (0x23) - .maxstack 5 + // Code size 21 (0x15) + .maxstack 2 .locals init (S1 V_0) IL_0000: nop IL_0001: call ""S1 Program.GetS1()"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloca.s V_0 - IL_0012: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" - IL_0017: call ""int Program.Get1()"" - IL_001c: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" - IL_0021: pop - IL_0022: ret + IL_000e: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0013: pop + IL_0014: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_03() + public void IndexerAccess_Get_RValueReceiver_03() { var src = """ -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, C1 x) - { - System.Console.Write(x.F1); - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(C1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -15010,7 +18188,7 @@ static void Main() static void Test() { - _ = GetC1()[Get1(), $"", Get1()]; + _ = GetC1()[Get1()]; } static C1 GetC1() => new C1 { F1 = 123 }; @@ -15022,56 +18200,35 @@ static int Get1() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (C1 V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: call ""C1 Program.GetC1()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(C1, int)"" + IL_0010: pop + IL_0011: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_04() + public void IndexerAccess_Get_RValueReceiver_04() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((S1)(object)x).F1); - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -15106,12 +18263,12 @@ static async Task Main() static void Test1() { - _ = GetT()[Get1(), $"", Get1()]; + _ = GetT()[Get1()]; } static void Test2() where T : struct { - _ = GetT()[Get1(), $"", Get1()]; + _ = GetT()[Get1()]; } static int Get1() @@ -15121,7 +18278,7 @@ static int Get1() static async Task Test3() { - _ = GetT()[Get1(), $"", await Get1Async()]; + _ = GetT()[await Get1Async()]; } static async Task Get1Async() @@ -15132,78 +18289,49 @@ static async Task Get1Async() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); verifier.VerifyIL("Program.Test2()", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_05() + public void IndexerAccess_Get_RValueReceiver_05() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) - { - System.Console.Write(((S1)(object)x).F1); - x = (TR)(object)new S1 { F1 = ((S1)(object)x).F1 + 1 }; - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -15232,7 +18360,7 @@ static async Task Main() static void Test2() where T : struct { - _ = GetT()[Get1(), $"", Get1()]; + _ = GetT()[Get1()]; } static T GetT() => (T)(object)new S1 { F1 = 123 }; @@ -15244,7 +18372,7 @@ static int Get1() static async Task Test3() where T : struct { - _ = GetT()[Get1(), $"", await Get1Async()]; + _ = GetT()[await Get1Async()]; } static async Task Get1Async() @@ -15255,55 +18383,38 @@ static async Task Get1Async() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2()", @" { - // Code size 35 (0x23) - .maxstack 5 + // Code size 21 (0x15) + .maxstack 2 .locals init (T V_0) IL_0000: nop IL_0001: call ""T Program.GetT()"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloca.s V_0 - IL_0012: newobj ""InterpolationHandler..ctor(int, int, ref T)"" - IL_0017: call ""int Program.Get1()"" - IL_001c: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" - IL_0021: pop - IL_0022: ret + IL_000e: call ""int E.get_Item(ref T, int)"" + IL_0013: pop + IL_0014: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_06() + public void IndexerAccess_Get_RValueReceiver_06() { var src = """ using System.Threading.Tasks; -[System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler -{ - - public InterpolationHandler(int literalLength, int formattedCount, TR x) - { - System.Console.Write(((C1)(object)x).F1); - } - public void AppendLiteral(string value) { } - public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; -} - static class E { extension(T x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i] { get { @@ -15338,12 +18449,12 @@ static async Task Main() static void Test1() { - _ = GetT()[Get1(), $"", Get1()]; + _ = GetT()[Get1()]; } static void Test2() where T : class { - _ = GetT()[Get1(), $"", Get1()]; + _ = GetT()[Get1()]; } static int Get1() @@ -15353,7 +18464,7 @@ static int Get1() static async Task Test3() { - _ = GetT()[Get1(), $"", await Get1Async()]; + _ = GetT()[await Get1Async()]; } static async Task Get1Async() @@ -15364,50 +18475,34 @@ static async Task Get1Async() } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123:123:123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); verifier.VerifyIL("Program.Test2()", @" { - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) + // Code size 18 (0x12) + .maxstack 2 IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_0006: call ""int Program.Get1()"" + IL_000b: call ""int E.get_Item(T, int)"" + IL_0010: pop + IL_0011: ret } "); } From 47f2aa84e41b895002b85220aa835fe4ca92a3f9 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 17 Jul 2025 13:09:09 -0700 Subject: [PATCH 4/9] Adjust a test --- .../Test/Emit3/Semantics/ExtensionTests2.cs | 181 +++++++++++------- 1 file changed, 107 insertions(+), 74 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 5b46be53c712a..7f4bc99ff8eba 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -11087,7 +11087,7 @@ struct S1 public void Test() { - this[0] += Program.Get1(); + this[Program.Get2()] += Program.Get1(); } } @@ -11110,7 +11110,7 @@ static void Main() static void Test() { - F[0] += Get1(); + F[Get2()] += Get1(); } public static int Get1() @@ -11118,57 +11118,69 @@ public static int Get1() Program.F.F1++; return 1; } + + public static int Get2() + { + Program.F.F1++; + return 1; + } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 39 (0x27) + // Code size 45 (0x2d) .maxstack 3 - .locals init (int V_0) + .locals init (int V_0, + int V_1) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: ldc.i4.0 - IL_000d: call ""int E.get_Item(S1, int)"" - IL_0012: call ""int Program.Get1()"" - IL_0017: add - IL_0018: stloc.0 - IL_0019: ldobj ""S1"" - IL_001e: ldc.i4.0 - IL_001f: ldloc.0 - IL_0020: call ""void E.set_Item(S1, int, int)"" - IL_0025: nop - IL_0026: ret + IL_0006: call ""int Program.Get2()"" + IL_000b: stloc.0 + IL_000c: dup + IL_000d: ldobj ""S1"" + IL_0012: ldloc.0 + IL_0013: call ""int E.get_Item(S1, int)"" + IL_0018: call ""int Program.Get1()"" + IL_001d: add + IL_001e: stloc.1 + IL_001f: ldobj ""S1"" + IL_0024: ldloc.0 + IL_0025: ldloc.1 + IL_0026: call ""void E.set_Item(S1, int, int)"" + IL_002b: nop + IL_002c: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 35 (0x23) + // Code size 41 (0x29) .maxstack 3 - .locals init (int V_0) + .locals init (int V_0, + int V_1) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: ldc.i4.0 - IL_0008: call ""int E.get_Item(S1, int)"" - IL_000d: call ""int Program.Get1()"" - IL_0012: add - IL_0013: stloc.0 - IL_0014: ldarg.0 - IL_0015: ldobj ""S1"" - IL_001a: ldc.i4.0 - IL_001b: ldloc.0 - IL_001c: call ""void E.set_Item(S1, int, int)"" - IL_0021: nop - IL_0022: ret + IL_0001: call ""int Program.Get2()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""int E.get_Item(S1, int)"" + IL_0013: call ""int Program.Get1()"" + IL_0018: add + IL_0019: stloc.1 + IL_001a: ldarg.0 + IL_001b: ldobj ""S1"" + IL_0020: ldloc.0 + IL_0021: ldloc.1 + IL_0022: call ""void E.set_Item(S1, int, int)"" + IL_0027: nop + IL_0028: ret } "); @@ -11233,7 +11245,7 @@ struct S1 public void Test() { - this[0] += Program.Get1(); + this[Program.Get2()] += Program.Get1(); } } @@ -11256,7 +11268,7 @@ static void Main() static void Test() { - F[0] += Get1(); + F[Get2()] += Get1(); } public static int Get1() @@ -11264,50 +11276,62 @@ public static int Get1() Program.F.F1++; return 1; } + + public static int Get2() + { + Program.F.F1++; + return 1; + } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 29 (0x1d) + // Code size 35 (0x23) .maxstack 4 - .locals init (S1& V_0) + .locals init (S1& V_0, + int V_1) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldc.i4.0 - IL_0009: ldloc.0 - IL_000a: ldc.i4.0 - IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" - IL_0010: call ""int Program.Get1()"" - IL_0015: add - IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, int)"" - IL_001b: nop - IL_001c: ret + IL_0007: call ""int Program.Get2()"" + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldloc.1 + IL_000f: ldloc.0 + IL_0010: ldloc.1 + IL_0011: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: add + IL_001c: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_0021: nop + IL_0022: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 23 (0x17) + // Code size 29 (0x1d) .maxstack 4 + .locals init (int V_0) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldc.i4.0 - IL_0003: ldarg.0 - IL_0004: ldc.i4.0 - IL_0005: call ""int E.get_Item(" + refKind + @" S1, int)"" - IL_000a: call ""int Program.Get1()"" - IL_000f: add - IL_0010: call ""void E.set_Item(" + refKind + @" S1, int, int)"" - IL_0015: nop - IL_0016: ret + IL_0001: call ""int Program.Get2()"" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: ldloc.0 + IL_0009: ldarg.0 + IL_000a: ldloc.0 + IL_000b: call ""int E.get_Item(" + refKind + @" S1, int)"" + IL_0010: call ""int Program.Get1()"" + IL_0015: add + IL_0016: call ""void E.set_Item(" + refKind + @" S1, int, int)"" + IL_001b: nop + IL_001c: ret } "); @@ -11381,7 +11405,7 @@ static void Main() static void Test() { - F[0] += Get1(); + F[Get2()] += Get1(); } static int Get1() @@ -11389,31 +11413,40 @@ static int Get1() Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } + + static int Get2() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 29 (0x1d) + // Code size 35 (0x23) .maxstack 4 - .locals init (C1 V_0) + .locals init (C1 V_0, + int V_1) IL_0000: nop IL_0001: ldsfld ""C1 Program.F"" IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldc.i4.0 - IL_0009: ldloc.0 - IL_000a: ldc.i4.0 - IL_000b: call ""int E.get_Item(C1, int)"" - IL_0010: call ""int Program.Get1()"" - IL_0015: add - IL_0016: call ""void E.set_Item(C1, int, int)"" - IL_001b: nop - IL_001c: ret + IL_0007: call ""int Program.Get2()"" + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldloc.1 + IL_000f: ldloc.0 + IL_0010: ldloc.1 + IL_0011: call ""int E.get_Item(C1, int)"" + IL_0016: call ""int Program.Get1()"" + IL_001b: add + IL_001c: call ""void E.set_Item(C1, int, int)"" + IL_0021: nop + IL_0022: ret } "); } From f4783671c7abd066d4f49bfb9e1a7eb331bdaa24 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 18 Jul 2025 11:51:01 -0700 Subject: [PATCH 5/9] Fix receiver capturing for various extension scenarios Fixes #79379 Fixes #79436 --- .../CSharp/Portable/CodeGen/EmitExpression.cs | 35 +- .../ExtensionMethodReferenceRewriter.cs | 14 +- .../LocalRewriter_AssignmentOperator.cs | 118 +- .../LocalRewriter/LocalRewriter_Call.cs | 110 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 120 +- ...writer_DeconstructionAssignmentOperator.cs | 2 +- .../LocalRewriter_IndexerAccess.cs | 59 +- ...writer_NullCoalescingAssignmentOperator.cs | 8 +- ...ObjectOrCollectionInitializerExpression.cs | 8 +- .../LocalRewriter_UnaryOperator.cs | 22 +- .../Portable/Lowering/SpillSequenceSpiller.cs | 7 +- .../CSharp/Test/Emit/CodeGen/IndexerTests.cs | 109 + .../Test/Emit3/Semantics/ExtensionTests.cs | 236 +- .../Test/Emit3/Semantics/ExtensionTests2.cs | 3031 +++++++++++------ 14 files changed, 2570 insertions(+), 1309 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 127509552c312..0fa1356951c24 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -720,28 +720,39 @@ private void EmitArgument(BoundExpression argument, RefKind refKind) EmitExpression(argument, true); break; - case RefKind.In: - var temp = EmitAddress(argument, AddressKind.ReadOnly); - AddExpressionTemp(temp); - break; - default: - Debug.Assert(refKind is RefKind.Ref or RefKind.Out or RefKindExtensions.StrictIn); - // NOTE: passing "ReadOnlyStrict" here. - // we should not get an address of a copy if at all possible - var unexpectedTemp = EmitAddress(argument, refKind == RefKindExtensions.StrictIn ? AddressKind.ReadOnlyStrict : AddressKind.Writeable); - if (unexpectedTemp != null) + Debug.Assert(refKind is RefKind.In or RefKind.Ref or RefKind.Out or RefKindExtensions.StrictIn); + var temp = EmitAddress(argument, GetArgumentAddressKind(refKind)); + if (temp != null) { // interestingly enough "ref dynamic" sometimes is passed via a clone // receiver of a ref field can be cloned too - Debug.Assert(argument.Type.IsDynamic() || argument is BoundFieldAccess { FieldSymbol.RefKind: not RefKind.None }, "passing args byref should not clone them into temps"); - AddExpressionTemp(unexpectedTemp); + Debug.Assert(refKind is RefKind.In || argument.Type.IsDynamic() || argument is BoundFieldAccess { FieldSymbol.RefKind: not RefKind.None }, "passing args byref should not clone them into temps"); + AddExpressionTemp(temp); } break; } } + internal static AddressKind GetArgumentAddressKind(RefKind refKind) + { + switch (refKind) + { + case RefKind.None: + throw ExceptionUtilities.UnexpectedValue(refKind); + + case RefKind.In: + return AddressKind.ReadOnly; + + default: + Debug.Assert(refKind is RefKind.Ref or RefKind.Out or RefKindExtensions.StrictIn); + // NOTE: returning "ReadOnlyStrict" here. + // we should not get an address of a copy if at all possible + return refKind == RefKindExtensions.StrictIn ? AddressKind.ReadOnlyStrict : AddressKind.Writeable; + } + } + private void EmitAddressOfExpression(BoundAddressOfOperator expression, bool used) { // NOTE: passing "ReadOnlyStrict" here. diff --git a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs index c06bd5efc479c..5b909e5a9bbaf 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs @@ -109,13 +109,13 @@ static BoundExpression updateCall( if (receiverRefKind != RefKind.None) { var builder = ArrayBuilder.GetInstance(method.ParameterCount, RefKind.None); - builder[0] = argumentRefKindFromReceiverRefKind(receiverRefKind); + builder[0] = ReceiverArgumentRefKindFromReceiverRefKind(receiverRefKind); argumentRefKinds = builder.ToImmutableAndFree(); } } else { - argumentRefKinds = argumentRefKinds.Insert(0, argumentRefKindFromReceiverRefKind(receiverRefKind)); + argumentRefKinds = argumentRefKinds.Insert(0, ReceiverArgumentRefKindFromReceiverRefKind(receiverRefKind)); } invokedAsExtensionMethod = true; @@ -141,14 +141,14 @@ static BoundExpression updateCall( boundCall.ResultKind, originalMethodsOpt, type); - - static RefKind argumentRefKindFromReceiverRefKind(RefKind receiverRefKind) - { - return SyntheticBoundNodeFactory.ArgumentRefKindFromParameterRefKind(receiverRefKind, useStrictArgumentRefKinds: false); - } } } + public static RefKind ReceiverArgumentRefKindFromReceiverRefKind(RefKind receiverRefKind) + { + return SyntheticBoundNodeFactory.ArgumentRefKindFromParameterRefKind(receiverRefKind, useStrictArgumentRefKinds: false); + } + [return: NotNullIfNotNull(nameof(method))] private static MethodSymbol? VisitMethodSymbolWithExtensionRewrite(BoundTreeRewriter rewriter, MethodSymbol? method) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index a87b77dbf5650..983b0e7166a3c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -81,7 +81,16 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo break; } - return MakeStaticAssignmentOperator(node.Syntax, loweredLeft, loweredRight, node.IsRef, used); + return MakeStaticAssignmentOperator(node.Syntax, loweredLeft, loweredRight, node.IsRef, used, AssignmentKind.SimpleAssignment); + } + + private enum AssignmentKind + { + SimpleAssignment, + CompoundAssignment, + IncrementDecrement, + Deconstruction, + NullCoalescingAssignment, } /// @@ -89,7 +98,7 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo /// Left and right sub-expressions must be in lowered form. /// private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, - bool used, bool isChecked, bool isCompoundAssignment) + bool used, bool isChecked, AssignmentKind assignmentKind) { switch (rewrittenLeft.Kind) { @@ -102,7 +111,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio indexerAccess.ArgumentNamesOpt, indexerAccess.ArgumentRefKindsOpt, rewrittenRight, - isCompoundAssignment, isChecked); + isCompoundAssignment: assignmentKind == AssignmentKind.CompoundAssignment, isChecked); case BoundKind.DynamicMemberAccess: var memberAccess = (BoundDynamicMemberAccess)rewrittenLeft; @@ -110,7 +119,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio memberAccess.Receiver, memberAccess.Name, rewrittenRight, - isCompoundAssignment, + isCompoundAssignment: assignmentKind == AssignmentKind.CompoundAssignment, isChecked).ToExpression(); case BoundKind.EventAccess: @@ -132,7 +141,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio throw ExceptionUtilities.Unreachable(); default: - return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, isRef: false, used: used); + return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, isRef: false, used: used, assignmentKind); } } @@ -168,7 +177,8 @@ private BoundExpression MakeStaticAssignmentOperator( BoundExpression rewrittenLeft, BoundExpression rewrittenRight, bool isRef, - bool used) + bool used, + AssignmentKind assignmentKind) { switch (rewrittenLeft.Kind) { @@ -192,7 +202,8 @@ private BoundExpression MakeStaticAssignmentOperator( false, default(ImmutableArray), rewrittenRight, - used); + used, + assignmentKind); } case BoundKind.IndexerAccess: @@ -212,7 +223,8 @@ private BoundExpression MakeStaticAssignmentOperator( indexerAccess.Expanded, indexerAccess.ArgsToParamsOpt, rewrittenRight, - used); + used, + assignmentKind); } case BoundKind.Local: @@ -248,7 +260,8 @@ private BoundExpression MakeStaticAssignmentOperator( sequence.Value, rewrittenRight, isRef, - used), + used, + assignmentKind), sequence.Type); } goto default; @@ -264,6 +277,17 @@ private BoundExpression MakeStaticAssignmentOperator( } } + private bool IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(BoundExpression rewrittenReceiver, PropertySymbol property) + { + return CanChangeValueBetweenReads(rewrittenReceiver, localsMayBeAssignedOrCaptured: true, structThisCanChangeValueBetweenReads: true) && + IsNewExtensionMemberWithByValPossiblyStructReceiver(property) && + CodeGen.CodeGenerator.HasHome(rewrittenReceiver, + CodeGen.CodeGenerator.AddressKind.ReadOnlyStrict, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null); + } + private BoundExpression MakePropertyAssignment( SyntaxNode syntax, BoundExpression? rewrittenReceiver, @@ -273,7 +297,8 @@ private BoundExpression MakePropertyAssignment( bool expanded, ImmutableArray argsToParamsOpt, BoundExpression rewrittenRight, - bool used) + bool used, + AssignmentKind assignmentKind) { // Rewrite property assignment into call to setter. var setMethod = property.GetOwnOrInheritedSetMethod(); @@ -293,25 +318,73 @@ private BoundExpression MakePropertyAssignment( } ArrayBuilder? argTempsBuilder = null; + + bool needSpecialExtensionPropertyReceiverReadOrder = false; + ArrayBuilder? storesOpt = null; + + if (rewrittenReceiver is not null && + assignmentKind is not (AssignmentKind.CompoundAssignment or AssignmentKind.NullCoalescingAssignment or AssignmentKind.Deconstruction or AssignmentKind.IncrementDecrement) && + IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, property) && + (arguments.Length != 0 || !IsSafeForReordering(rewrittenRight, RefKind.None))) + { + // The receiever has location, but extension property/indexer takes receiver by value. + // This means that we need to ensure that the the receiver value is read after + // any side-effecting arguments and right hand side are evaluated, so that the + // the setter receives the last value of the receiver, not the value before the + // arguments/rhs were evaluated. Receiver sideeffects should be evaluated at + // the very beginning, of course. + + needSpecialExtensionPropertyReceiverReadOrder = true; + storesOpt = ArrayBuilder.GetInstance(); + } + +#if DEBUG + BoundExpression? rewrittenReceiverBeforePossibleCapture = rewrittenReceiver; +#endif arguments = VisitArgumentsAndCaptureReceiverIfNeeded( ref rewrittenReceiver, - forceReceiverCapturing: false, + forceReceiverCapturing: needSpecialExtensionPropertyReceiverReadOrder, arguments, property, argsToParamsOpt, argumentRefKindsOpt, - storesOpt: null, + storesOpt, ref argTempsBuilder); - arguments = MakeArguments( - arguments, - property, - expanded, - argsToParamsOpt, - ref argumentRefKindsOpt, - ref argTempsBuilder, - invokedAsExtensionMethod: false); + if (needSpecialExtensionPropertyReceiverReadOrder) + { +#if DEBUG + Debug.Assert(rewrittenReceiverBeforePossibleCapture != (object?)rewrittenReceiver); +#endif + Debug.Assert(storesOpt is { }); + Debug.Assert(storesOpt.Count != 0); + Debug.Assert(argTempsBuilder is not null); + + // Store everything that is non-trivial into a temporary; record the + // stores in storesToTemps and make the actual argument a reference to the temp. + arguments = ExtractSideEffectsFromArguments(arguments, property, expanded, argsToParamsOpt, ref argumentRefKindsOpt, storesOpt, argTempsBuilder); + + if (!IsSafeForReordering(rewrittenRight, RefKind.None)) + { + BoundLocal capturedRight = _factory.StoreToTemp(rewrittenRight, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); + argTempsBuilder.Add(capturedRight.LocalSymbol); + storesOpt.Add(assignmentToTemp); + rewrittenRight = capturedRight; + } + } + else + { + arguments = MakeArguments( + arguments, + property, + expanded, + argsToParamsOpt, + ref argumentRefKindsOpt, + ref argTempsBuilder, + invokedAsExtensionMethod: false); + } + var sideEffects = storesOpt is null ? [] : storesOpt.ToImmutableAndFree(); var argTemps = argTempsBuilder.ToImmutableAndFree(); if (used) @@ -342,7 +415,7 @@ private BoundExpression MakePropertyAssignment( return new BoundSequence( syntax, AppendToPossibleNull(argTemps, rhsTemp), - ImmutableArray.Create(setterCall), + sideEffects.Add(setterCall), // https://github.com/dotnet/roslyn/issues/78829 - there is no test coverage for sideEffects on this code path boundRhs, rhsTemp.Type); } @@ -357,6 +430,7 @@ private BoundExpression MakePropertyAssignment( if (argTemps.IsDefaultOrEmpty) { + Debug.Assert(sideEffects.IsEmpty); return setterCall; } else @@ -364,7 +438,7 @@ private BoundExpression MakePropertyAssignment( return new BoundSequence( syntax, argTemps, - ImmutableArray.Empty, + sideEffects, setterCall, setMethod.ReturnType); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index eea7d4bdaa927..3421da09193fe 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -644,6 +644,18 @@ internal static bool IsCapturedPrimaryConstructorParameter(BoundExpression expre /// /// Visits all arguments of a method, doing any necessary rewriting for interpolated string handler conversions that /// might be present in the arguments and creating temps for any discard parameters. + /// + /// When is true (which means the receiver must be captured regardless of + /// interpolated string handler conversions needs), must be not null. + /// + /// If receiver is captured by this method: + /// - If is not null, the sideeffect of capturing is added to + /// and is changed to the captured value; + /// - Otherwise, is changed to a node with no locals, + /// the sideeffects of capturing are the sifeeffects of the sequence and its result is the captured value. + /// + /// All temps introduced by this function for capturing purposes (including the temp capturing the receiver) are appended + /// to , which is allocated, if 'null' on input. /// private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded( [NotNullIfNotNull(nameof(rewrittenReceiver))] ref BoundExpression? rewrittenReceiver, @@ -674,44 +686,48 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded RefKind refKind; - if (forceReceiverCapturing) + if (methodOrIndexer.GetIsNewExtensionMember()) { - // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), - // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. - // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable - // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) - // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed - // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, - // SPEC VIOLATION: as value types. - - refKind = (methodOrIndexer.GetIsNewExtensionMember() ? - !rewrittenReceiver.Type.IsReferenceType : - rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter) ? - RefKind.Ref : RefKind.None; + refKind = GetNewExtensionMemberReceiverCaptureRefKind(rewrittenReceiver, methodOrIndexer); } else { - if (rewrittenReceiver.Type.IsReferenceType) // IsNewExtensionMemberAccessWithByValPossiblyStructReceiver + if (forceReceiverCapturing) { - refKind = RefKind.None; + // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), + // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. + // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable + // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) + // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed + // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, + // SPEC VIOLATION: as value types. + + refKind = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter ? RefKind.Ref : RefKind.None; } else { - refKind = rewrittenReceiver.GetRefKind(); - - if (refKind == RefKind.None && - CodeGenerator.HasHome(rewrittenReceiver, - CodeGenerator.AddressKind.Constrained, - _factory.CurrentFunction, - peVerifyCompatEnabled: false, - stackLocalsOpt: null)) + if (rewrittenReceiver.Type.IsReferenceType) + { + refKind = RefKind.None; + } + else { - refKind = RefKind.Ref; + refKind = rewrittenReceiver.GetRefKind(); + + if (refKind == RefKind.None && + CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.AddressKind.Constrained, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null)) + { + refKind = RefKind.Ref; + } } } } - receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind); + receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, (refKind is RefKind.RefReadOnlyParameter or RefKind.In) ? RefKindExtensions.StrictIn : refKind); tempsOpt ??= ArrayBuilder.GetInstance(); tempsOpt.Add(receiverTemp.LocalSymbol); @@ -929,6 +945,48 @@ static bool usesReceiver(BoundExpression argument) } } + private RefKind GetNewExtensionMemberReceiverCaptureRefKind(BoundExpression rewrittenReceiver, Symbol methodOrIndexer) + { + Debug.Assert(rewrittenReceiver.Type is { }); + Debug.Assert(methodOrIndexer.ContainingType.ExtensionParameter is { }); + + RefKind receiverRefKind = methodOrIndexer.ContainingType.ExtensionParameter.RefKind; + bool isReceiverTakenByValue = receiverRefKind == RefKind.None; + + if (rewrittenReceiver.Type.IsReferenceType || + (isReceiverTakenByValue && methodOrIndexer is MethodSymbol)) // Extension methods with by-value receivers capture by value as classic extension methods do. + { + return RefKind.None; + } + + if (isReceiverTakenByValue) + { + if (CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.AddressKind.ReadOnlyStrict, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null)) + { + return RefKindExtensions.StrictIn; + } + + return RefKind.None; + } + + RefKind refKind = ExtensionMethodReferenceRewriter.ReceiverArgumentRefKindFromReceiverRefKind(receiverRefKind); + + if (CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.GetArgumentAddressKind(refKind), + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null)) + { + return refKind; + } + + return RefKind.None; + } + private void ReferToTempIfReferenceTypeReceiver(BoundLocal receiverTemp, ref BoundAssignmentOperator assignmentToTemp, out BoundAssignmentOperator? extraRefInitialization, ArrayBuilder temps) { Debug.Assert(assignmentToTemp.IsRef); @@ -950,7 +1008,7 @@ private void ReferToTempIfReferenceTypeReceiver(BoundLocal receiverTemp, ref Bou if (!receiverType.IsReferenceType) { // Store receiver ref to a different ref local - intermediate ref - var intermediateRef = _factory.Local(_factory.SynthesizedLocal(receiverType, refKind: RefKind.Ref)); + var intermediateRef = _factory.Local(_factory.SynthesizedLocal(receiverType, refKind: receiverTemp.LocalSymbol.RefKind)); temps.Add(intermediateRef.LocalSymbol); extraRefInitialization = assignmentToTemp.Update(intermediateRef, assignmentToTemp.Right, assignmentToTemp.IsRef, assignmentToTemp.Type); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 7925bc407f113..e309736925d02 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -76,7 +76,7 @@ private BoundExpression VisitInstanceCompoundAssignmentOperator(BoundCompoundAss Debug.Assert(node.LeftConversion is null); - return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Left, node.Right, node.Operator.Method, node.Operator.Kind.IsChecked()); + return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Left, node.Right, node.Operator.Method, node.Operator.Kind.IsChecked(), AssignmentKind.CompoundAssignment); } private BoundExpression VisitBuiltInOrStaticCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used) @@ -231,12 +231,12 @@ BoundExpression rewriteAssignment(BoundExpression leftRead, BoundExpression righ { // We need to create a tree that ensures that receiver of 'set' is evaluated after the binary operation BoundLocal binaryResult = _factory.StoreToTemp(opFinal, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); - BoundExpression assignment = MakeAssignmentOperator(syntax, transformedLHS, binaryResult, used: used, isChecked: isChecked, isCompoundAssignment: true); + BoundExpression assignment = MakeAssignmentOperator(syntax, transformedLHS, binaryResult, used: used, isChecked: isChecked, AssignmentKind.CompoundAssignment); Debug.Assert(assignment.Type is { }); return new BoundSequence(syntax, [binaryResult.LocalSymbol], [assignmentToTemp], assignment, assignment.Type); } - return MakeAssignmentOperator(syntax, transformedLHS, opFinal, used: used, isChecked: isChecked, isCompoundAssignment: true); + return MakeAssignmentOperator(syntax, transformedLHS, opFinal, used: used, isChecked: isChecked, AssignmentKind.CompoundAssignment); } } @@ -245,17 +245,17 @@ private static bool IsNewExtensionMemberAccessWithByValPossiblyStructReceiver(Bo switch (transformedLHS) { case BoundPropertyAccess { PropertySymbol: { } property }: - return checkProperty(property); + return IsNewExtensionMemberWithByValPossiblyStructReceiver(property); case BoundIndexerAccess { Indexer: { } indexer }: - return checkProperty(indexer); + return IsNewExtensionMemberWithByValPossiblyStructReceiver(indexer); default: return false; } + } - static bool checkProperty(PropertySymbol property) - { - return property.GetIsNewExtensionMember() && !property.IsStatic && property.ContainingType.ExtensionParameter is { RefKind: RefKind.None, Type.IsReferenceType: false }; - } + static bool IsNewExtensionMemberWithByValPossiblyStructReceiver(Symbol symbol) + { + return symbol.GetIsNewExtensionMember() && !symbol.IsStatic && symbol.ContainingType.ExtensionParameter is { RefKind: RefKind.None, Type.IsReferenceType: false }; } private BoundExpression? TransformPropertyOrEventReceiver(Symbol propertyOrEvent, BoundExpression? receiverOpt, ArrayBuilder stores, ArrayBuilder temps) @@ -289,31 +289,43 @@ static bool checkProperty(PropertySymbol property) Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression); BoundExpression rewrittenReceiver = VisitExpression(receiverOpt); + Debug.Assert(rewrittenReceiver.Type is { }); BoundAssignmentOperator assignmentToTemp; + RefKind refKind; + bool isKnownToReferToTempIfReferenceType = false; - // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), - // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. - // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable - // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) - // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed - // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, - // SPEC VIOLATION: as value types. - Debug.Assert(rewrittenReceiver.Type is { }); - var variableRepresentsLocation = propertyOrEvent.GetIsNewExtensionMember() ? - !rewrittenReceiver.Type.IsReferenceType : - (rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter); + if (propertyOrEvent.GetIsNewExtensionMember()) + { + refKind = GetNewExtensionMemberReceiverCaptureRefKind(rewrittenReceiver, propertyOrEvent); + } + else + { + // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), + // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. + // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable + // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) + // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed + // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, + // SPEC VIOLATION: as value types. + + var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter; + refKind = variableRepresentsLocation ? RefKind.Ref : RefKind.None; + + isKnownToReferToTempIfReferenceType = !variableRepresentsLocation || rewrittenReceiver.Type.IsValueType || + !CodeGenerator.HasHome(rewrittenReceiver, + CodeGenerator.AddressKind.Constrained, + _factory.CurrentFunction, + peVerifyCompatEnabled: false, + stackLocalsOpt: null); + } var receiverTemp = _factory.StoreToTemp( rewrittenReceiver, out assignmentToTemp, - refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None, - isKnownToReferToTempIfReferenceType: !variableRepresentsLocation || rewrittenReceiver.Type.IsValueType || - !CodeGenerator.HasHome(rewrittenReceiver, - CodeGenerator.AddressKind.Constrained, - _factory.CurrentFunction, - peVerifyCompatEnabled: false, - stackLocalsOpt: null)); + refKind: refKind, + isKnownToReferToTempIfReferenceType: isKnownToReferToTempIfReferenceType); + temps.Add(receiverTemp.LocalSymbol); if (receiverTemp.LocalSymbol.IsRef && @@ -433,7 +445,38 @@ private BoundIndexerAccess TransformIndexerAccessContinued( PropertySymbol indexer = indexerAccess.Indexer; bool expanded = indexerAccess.Expanded; + rewrittenArguments = ExtractSideEffectsFromArguments(rewrittenArguments, indexer, expanded, argsToParamsOpt, ref argumentRefKinds, stores, temps); + // This is a temporary object that will be rewritten away before the lowering completes. + return new BoundIndexerAccess( + syntax, + transformedReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + indexer, + rewrittenArguments, + argumentNamesOpt: default(ImmutableArray), + argumentRefKinds, + expanded: false, + accessorKind: indexerAccess.AccessorKind, + argsToParamsOpt: default(ImmutableArray), + defaultArguments: default(BitVector), + indexerAccess.Type); + } + + /// + /// Store everything that is non-trivial into a temporary; record the + /// stores in and make the actual argument a reference to the temp. + /// Return resulting arguments. + /// + private ImmutableArray ExtractSideEffectsFromArguments( + ImmutableArray rewrittenArguments, + PropertySymbol indexer, + bool expanded, + ImmutableArray argsToParamsOpt, + ref ImmutableArray argumentRefKinds, + ArrayBuilder stores, + ArrayBuilder temps) + { ImmutableArray parameters = indexer.Parameters; BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter. ArrayBuilder storesToTemps = ArrayBuilder.GetInstance(rewrittenArguments.Length); @@ -463,16 +506,10 @@ private BoundIndexerAccess TransformIndexerAccessContinued( BoundAssignmentOperator storeToTemp; var boundTemp = _factory.StoreToTemp(array, out storeToTemp); - stores.Add(storeToTemp); - temps.Add(boundTemp.LocalSymbol); + storesToTemps.Add(storeToTemp); actualArguments[actualArguments.Length - 1] = boundTemp; } - // Step three: Now fill in the optional arguments. (Dev11 uses the getter for optional arguments in - // compound assignments, but for deconstructions we use the setter if the getter is missing.) - var accessor = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod(); - Debug.Assert(accessor is not null); - // For a call, step four would be to optimize away some of the temps. However, we need them all to prevent // duplicate side-effects, so we'll skip that step. @@ -494,20 +531,7 @@ private BoundIndexerAccess TransformIndexerAccessContinued( argumentRefKinds = GetRefKindsOrNull(refKinds); refKinds.Free(); - // This is a temporary object that will be rewritten away before the lowering completes. - return new BoundIndexerAccess( - syntax, - transformedReceiver, - initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, - indexer, - rewrittenArguments, - argumentNamesOpt: default(ImmutableArray), - argumentRefKinds, - expanded: false, - accessorKind: indexerAccess.AccessorKind, - argsToParamsOpt: default(ImmutableArray), - defaultArguments: default(BitVector), - indexerAccess.Type); + return rewrittenArguments; } private BoundExpression TransformImplicitIndexerAccess( diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs index 8b1a73e3edd84..14caaad3e0e44 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs @@ -252,7 +252,7 @@ static bool canReorderTargetAssignments(ArrayBuilder? temps = null; + bool needSpecialExtensionPropertyReceiverReadOrder = false; + ArrayBuilder? storesOpt = null; + + if (IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, indexer)) + { + // The receiever has location, but extension indexer takes receiver by value. + // This means that we need to ensure that the the receiver value is read after + // any side-effecting arguments are evaluated, so that the + // the setter receives the last value of the receiver, not the value before the + // arguments were evaluated. Receiver sideeffects should be evaluated at + // the very beginning, of course. + + needSpecialExtensionPropertyReceiverReadOrder = true; + storesOpt = ArrayBuilder.GetInstance(); + } + +#if DEBUG + BoundExpression? rewrittenReceiverBeforePossibleCapture = rewrittenReceiver; +#endif ImmutableArray rewrittenArguments = VisitArgumentsAndCaptureReceiverIfNeeded( ref rewrittenReceiver, - forceReceiverCapturing: false, + forceReceiverCapturing: needSpecialExtensionPropertyReceiverReadOrder, arguments, indexer, argsToParamsOpt, argumentRefKindsOpt, - storesOpt: null, + storesOpt, ref temps); - rewrittenArguments = MakeArguments( - rewrittenArguments, - indexer, - expanded, - argsToParamsOpt, - ref argumentRefKindsOpt, - ref temps); + if (needSpecialExtensionPropertyReceiverReadOrder) + { +#if DEBUG + Debug.Assert(rewrittenReceiverBeforePossibleCapture != (object?)rewrittenReceiver); +#endif + Debug.Assert(storesOpt is { }); + Debug.Assert(storesOpt.Count != 0); + Debug.Assert(temps is not null); + + // Store everything that is non-trivial into a temporary; record the + // stores in storesToTemps and make the actual argument a reference to the temp. + rewrittenArguments = ExtractSideEffectsFromArguments(rewrittenArguments, indexer, expanded, argsToParamsOpt, ref argumentRefKindsOpt, storesOpt, temps); + } + else + { + rewrittenArguments = MakeArguments( + rewrittenArguments, + indexer, + expanded, + argsToParamsOpt, + ref argumentRefKindsOpt, + ref temps); + } + var sideEffects = storesOpt is null ? [] : storesOpt.ToImmutableAndFree(); BoundExpression call = MakePropertyGetAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, argumentRefKindsOpt, getMethod); Debug.Assert(call.Type is not null); if (temps.Count == 0) { + Debug.Assert(sideEffects.IsEmpty); temps.Free(); return call; } @@ -189,7 +228,7 @@ private BoundExpression MakeIndexerAccess( return new BoundSequence( syntax, temps.ToImmutableAndFree(), - ImmutableArray.Empty, + sideEffects, call, call.Type); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs index 600fd56b4b89e..cfe3faf35e248 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs @@ -45,13 +45,13 @@ BoundExpression rewriteNullCoalscingAssignmentStandard() { // We need to create a tree that ensures that receiver of 'set' is evaluated after the right hand side value BoundLocal rightResult = _factory.StoreToTemp(loweredRight, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); - assignment = MakeAssignmentOperator(syntax, transformedLHS, rightResult, used: true, isChecked: false, isCompoundAssignment: false); + assignment = MakeAssignmentOperator(syntax, transformedLHS, rightResult, used: true, isChecked: false, AssignmentKind.NullCoalescingAssignment); Debug.Assert(assignment.Type is { }); assignment = new BoundSequence(syntax, [rightResult.LocalSymbol], [assignmentToTemp], assignment, assignment.Type); } else { - assignment = MakeAssignmentOperator(syntax, transformedLHS, loweredRight, used: true, isChecked: false, isCompoundAssignment: false); + assignment = MakeAssignmentOperator(syntax, transformedLHS, loweredRight, used: true, isChecked: false, AssignmentKind.NullCoalescingAssignment); } // lhsRead ?? (transformedLHS = loweredRight) @@ -120,7 +120,7 @@ BoundExpression rewriteNullCoalescingAssignmentForValueType() temps.Add(tmp.LocalSymbol); // tmp = loweredRight; - var tmpAssignment = MakeAssignmentOperator(node.Syntax, tmp, loweredRight, used: true, isChecked: false, isCompoundAssignment: false); + var tmpAssignment = MakeAssignmentOperator(node.Syntax, tmp, loweredRight, used: true, isChecked: false, AssignmentKind.SimpleAssignment); Debug.Assert(transformedLHS.Type.GetNullableUnderlyingType().Equals(tmp.Type.StrippedType(), TypeCompareKind.AllIgnoreOptions)); @@ -133,7 +133,7 @@ BoundExpression rewriteNullCoalescingAssignmentForValueType() MakeConversionNode(tmp, transformedLHS.Type, @checked: false, markAsChecked: true), used: true, isChecked: false, - isCompoundAssignment: false); + AssignmentKind.NullCoalescingAssignment); // lhsRead.HasValue var lhsReadHasValue = BoundCall.Synthesized(leftOperand.Syntax, lhsRead, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, hasValue); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs index 0ec53590e2ecc..b6189ff435b1d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs @@ -398,7 +398,7 @@ private void AddObjectInitializer( // Rewrite simple assignment to field/property. var rewrittenRight = VisitExpression(right); Debug.Assert(assignment.Type.IsDynamic() || TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: assignment.IsRef, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: assignment.IsRef, used: false, AssignmentKind.SimpleAssignment)); return; } } @@ -470,7 +470,7 @@ private void AddObjectInitializer( // Rewrite simple assignment to field/property. var rewrittenRight = VisitExpression(right); Debug.Assert(TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false, AssignmentKind.SimpleAssignment)); return; } @@ -503,7 +503,7 @@ private void AddObjectInitializer( // Rewrite as simple assignment. var rewrittenRight = VisitExpression(right); Debug.Assert(TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, used: false, AssignmentKind.SimpleAssignment)); return; } @@ -537,7 +537,7 @@ private void AddObjectInitializer( { var rewrittenRight = VisitExpression(right); Debug.Assert(TypeSymbol.Equals(rewrittenAccess.Type, assignment.Type, TypeCompareKind.AllIgnoreOptions)); - result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: false, used: false)); + result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, isRef: false, used: false, AssignmentKind.SimpleAssignment)); return; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs index fd76f5c2b6202..7b60f3b3289c2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs @@ -450,10 +450,10 @@ private BoundExpression VisitInstanceIncrementOperator(BoundIncrementOperator no Debug.Assert(node.OperandConversion is null); - return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Operand, rightOpt: null, node.MethodOpt, node.OperatorKind.IsChecked()); + return MakeInstanceCompoundAssignmentOperatorResult(node.Syntax, node.Operand, rightOpt: null, node.MethodOpt, node.OperatorKind.IsChecked(), AssignmentKind.IncrementDecrement); } - private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode syntax, BoundExpression left, BoundExpression? rightOpt, MethodSymbol operatorMethod, bool isChecked) + private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode syntax, BoundExpression left, BoundExpression? rightOpt, MethodSymbol operatorMethod, bool isChecked, AssignmentKind assignmentKind) { TypeSymbol? operandType = left.Type; //type of the variable being incremented Debug.Assert(operandType is { }); @@ -479,7 +479,7 @@ private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode if (operandType.IsValueType) { BoundCall increment = makeIncrementCall(syntax, boundTemp, rightOpt, operatorMethod); - BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked); + BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked, assignmentKind); tempInitializers.Add(increment); tempInitializers.Add(assignBack); @@ -495,7 +495,7 @@ private BoundExpression MakeInstanceCompoundAssignmentOperatorResult(SyntaxNode } BoundCall increment = makeIncrementCall(syntax, boundTemp, rightOpt, operatorMethod); - BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked); + BoundExpression assignBack = makeAssignmentBack(syntax, transformedLHS, boundTemp, isChecked, assignmentKind); // (object)default(T) != null var isNotClass = _factory.IsNotNullReference(_factory.Default(operandType)); @@ -525,9 +525,9 @@ static BoundCall makeIncrementCall(SyntaxNode syntax, BoundLocal boundTemp, Boun return BoundCall.Synthesized(syntax, boundTemp, initialBindingReceiverIsSubjectToCloning: ThreeState.False, operatorMethod, rightOpt is null ? [] : [rightOpt]); } - BoundExpression makeAssignmentBack(SyntaxNode syntax, BoundExpression transformedLHS, BoundLocal boundTemp, bool isChecked) + BoundExpression makeAssignmentBack(SyntaxNode syntax, BoundExpression transformedLHS, BoundLocal boundTemp, bool isChecked, AssignmentKind assignmentKind) { - return MakeAssignmentOperator(syntax, transformedLHS, boundTemp, used: false, isChecked: isChecked, isCompoundAssignment: false); + return MakeAssignmentOperator(syntax, transformedLHS, boundTemp, used: false, isChecked: isChecked, assignmentKind); } } @@ -661,7 +661,7 @@ BoundExpression rewriteWithNotRefOperand( // prefix: temp = (X)(T.Increment((T)operand))); operand = temp; // postfix: temp = operand; operand = (X)(T.Increment((T)temp))); - tempInitializers.Add(MakeAssignmentOperator(syntax, boundTemp, isPrefix ? newValue : MakeRValue(transformedLHS), used: false, isChecked: isChecked, isCompoundAssignment: false)); + tempInitializers.Add(MakeAssignmentOperator(syntax, boundTemp, isPrefix ? newValue : MakeRValue(transformedLHS), used: false, isChecked: isChecked, AssignmentKind.SimpleAssignment)); if (!isPrefix && IsNewExtensionMemberAccessWithByValPossiblyStructReceiver(transformedLHS)) { @@ -669,11 +669,11 @@ BoundExpression rewriteWithNotRefOperand( BoundLocal incrementResult = _factory.StoreToTemp(newValue, out BoundAssignmentOperator assignmentToTemp, refKind: RefKind.None); tempSymbols.Add(incrementResult.LocalSymbol); tempInitializers.Add(assignmentToTemp); - tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, incrementResult, used: false, isChecked: isChecked, isCompoundAssignment: false)); + tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, incrementResult, used: false, isChecked: isChecked, AssignmentKind.IncrementDecrement)); } else { - tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, isPrefix ? boundTemp : newValue, used: false, isChecked: isChecked, isCompoundAssignment: false)); + tempInitializers.Add(MakeAssignmentOperator(syntax, transformedLHS, isPrefix ? boundTemp : newValue, used: false, isChecked: isChecked, AssignmentKind.IncrementDecrement)); } // prefix: Seq( operand initializers; temp = (T)(operand + 1); operand = temp; result: temp) @@ -700,7 +700,7 @@ BoundExpression rewriteWithRefOperand( var tempValue = isPrefix ? newValue : MakeRValue(operand); Debug.Assert(tempValue.Type is { }); - var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, used: false, isChecked: isChecked, isCompoundAssignment: false); + var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, used: false, isChecked: isChecked, AssignmentKind.SimpleAssignment); var operandValue = isPrefix ? boundTemp : newValue; var tempAssignedAndOperandValue = new BoundSequence( @@ -712,7 +712,7 @@ BoundExpression rewriteWithRefOperand( // prefix: operand = Seq{temp = (T)(operand + 1); temp;} // postfix: operand = Seq{temp = operand; ; (T)(temp + 1);} - BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, used: false, isChecked: isChecked, isCompoundAssignment: false); + BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, used: false, isChecked: isChecked, AssignmentKind.IncrementDecrement); // prefix: Seq{operand initializers; operand = Seq{temp = (T)(operand + 1); temp;} result: temp} // postfix: Seq{operand initializers; operand = Seq{temp = operand; ; (T)(temp + 1);} result: temp} diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index 66fae04bf79cc..9607380fe911c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -535,10 +535,10 @@ internal static bool IsComplexConditionalInitializationOfReceiverRef( if (assignment is { IsRef: true, - Left: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.Ref } receiverRefLocal }, + Left: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: not RefKind.None } receiverRefLocal }, Right: BoundComplexConditionalReceiver { - ValueTypeReceiver: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.Ref } } valueTypeReceiver, + ValueTypeReceiver: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: not RefKind.None } } valueTypeReceiver, ReferenceTypeReceiver: BoundSequence { Locals.IsEmpty: true, @@ -548,7 +548,7 @@ internal static bool IsComplexConditionalInitializationOfReceiverRef( { IsRef: false, Left: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.None } referenceTypeClone }, - Right: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.Ref } originalReceiverReference } + Right: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: not RefKind.None } originalReceiverReference } } ], Value: BoundLocal { LocalSymbol: { SynthesizedKind: SynthesizedLocalKind.LoweringTemp, RefKind: RefKind.None } } referenceTypeReceiver @@ -563,6 +563,7 @@ internal static bool IsComplexConditionalInitializationOfReceiverRef( && !receiverRefLocal.Type.IsReferenceType && !receiverRefLocal.Type.IsValueType && valueTypeReceiver.Type.Equals(receiverRefLocal.Type, TypeCompareKind.AllIgnoreOptions) + && receiverRefLocal.RefKind == valueTypeReceiver.LocalSymbol.RefKind && referenceTypeReceiver.Type.Equals(receiverRefLocal.Type, TypeCompareKind.AllIgnoreOptions) ) { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs index 6b01995d2246c..91e546fcc8f06 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs @@ -1261,5 +1261,114 @@ static IEnumerable Test() comp1.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor); CompileAndVerify(comp1).VerifyDiagnostics(); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79436")] + public void ParamArrayOrderOfEvaluation() + { + var text1 = @" +public struct S1 +{ + public int this[int i, params int[] j] + { + get + { + System.Console.Write(""get_Item ""); + System.Console.Write(i); + System.Console.Write(j[0]); + System.Console.Write("" ""); + return 0; + } + set + { + System.Console.Write(""set_Item ""); + System.Console.Write(i); + System.Console.Write(j[0]); + System.Console.Write(value); + } + } +} + +class Program +{ + static void Main() + { + Test1(default); + System.Console.WriteLine(); + Test2(default); + } + + static void Test1(S1 s) + { + s[Get1(), Get2()] += Get3(); + } + + static void Test2(S1 s) + { + s[Get1(), [Get2()]] += Get3(); + } + + public static int Get1() + { + System.Console.Write(""Get1 ""); + return 1; + } + + public static int Get2() + { + System.Console.Write(""Get2 ""); + return 2; + } + + public static int Get3() + { + System.Console.Write(""Get3 ""); + return 3; + } +}"; + var comp1 = CreateCompilation(text1, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp1, expectedOutput: @" +Get1 Get2 get_Item 12 Get3 set_Item 123 +Get1 Get2 get_Item 12 Get3 set_Item 123 +").VerifyDiagnostics(); + + var expectedIL = @" +{ + // Code size 49 (0x31) + .maxstack 6 + .locals init (S1& V_0, + int V_1, + int[] V_2) + IL_0000: nop + IL_0001: ldarga.s V_0 + IL_0003: stloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: stloc.1 + IL_000a: ldc.i4.1 + IL_000b: newarr ""int"" + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: call ""int Program.Get2()"" + IL_0017: stelem.i4 + IL_0018: stloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: ldloc.0 + IL_001d: ldloc.1 + IL_001e: ldloc.2 + IL_001f: call ""int S1.this[int, params int[]].get"" + IL_0024: call ""int Program.Get3()"" + IL_0029: add + IL_002a: call ""void S1.this[int, params int[]].set"" + IL_002f: nop + IL_0030: ret +} +"; + + verifier.VerifyIL("Program.Test1", expectedIL); + + verifier.VerifyIL("Program.Test2", expectedIL); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index 642073dc6039d..d375b7ef5eec8 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -21730,38 +21730,34 @@ public void M(int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerA int Increment() => E.field.i++; """; - // Should be 0033, https://github.com/dotnet/roslyn/issues/79379 - var expectedOutput = "1033"; + var expectedOutput = "0033"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("", """ { - // Code size 61 (0x3d) + // Code size 51 (0x33) .maxstack 5 - .locals init (MyStruct& V_0, - MyStruct V_1) - IL_0000: ldsflda "MyStruct E.field" + .locals init (MyStruct V_0) + IL_0000: ldsfld "MyStruct E.field" IL_0005: stloc.0 IL_0006: ldloc.0 - IL_0007: ldobj "MyStruct" - IL_000c: call "int Program.<
$>g__Increment|0_0()" - IL_0011: ldc.i4.0 - IL_0012: ldc.i4.0 - IL_0013: ldloc.0 - IL_0014: ldobj "MyStruct" - IL_0019: newobj "InterpolationHandler..ctor(int, int, MyStruct)" - IL_001e: call "void E.M(MyStruct, int, InterpolationHandler)" - IL_0023: ldsfld "MyStruct E.field" - IL_0028: stloc.1 - IL_0029: ldloc.1 - IL_002a: call "int Program.<
$>g__Increment|0_0()" - IL_002f: ldc.i4.0 - IL_0030: ldc.i4.0 - IL_0031: ldloc.1 - IL_0032: newobj "InterpolationHandler..ctor(int, int, MyStruct)" - IL_0037: call "void E.M(MyStruct, int, InterpolationHandler)" - IL_003c: ret + IL_0007: call "int Program.<
$>g__Increment|0_0()" + IL_000c: ldc.i4.0 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: newobj "InterpolationHandler..ctor(int, int, MyStruct)" + IL_0014: call "void E.M(MyStruct, int, InterpolationHandler)" + IL_0019: ldsfld "MyStruct E.field" + IL_001e: stloc.0 + IL_001f: ldloc.0 + IL_0020: call "int Program.<
$>g__Increment|0_0()" + IL_0025: ldc.i4.0 + IL_0026: ldc.i4.0 + IL_0027: ldloc.0 + IL_0028: newobj "InterpolationHandler..ctor(int, int, MyStruct)" + IL_002d: call "void E.M(MyStruct, int, InterpolationHandler)" + IL_0032: ret } """); @@ -21847,46 +21843,11 @@ static void Test4() where T : struct } """; - var expectedOutput = "10337699"; + var expectedOutput = "00336699"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); - verifier.VerifyIL("Porgram.Test1", """ - { - // Code size 65 (0x41) - .maxstack 5 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) - IL_0000: ldsflda "T E.field" - IL_0005: stloc.2 - IL_0006: ldloca.s V_3 - IL_0008: initobj "T" - IL_000e: ldloc.3 - IL_000f: box "T" - IL_0014: brtrue.s IL_0021 - IL_0016: ldloc.2 - IL_0017: ldobj "T" - IL_001c: stloc.1 - IL_001d: ldloca.s V_1 - IL_001f: br.s IL_0022 - IL_0021: ldloc.2 - IL_0022: stloc.0 - IL_0023: ldloc.0 - IL_0024: ldobj "T" - IL_0029: call "int Porgram.Increment()" - IL_002e: ldc.i4.0 - IL_002f: ldc.i4.0 - IL_0030: ldloc.0 - IL_0031: ldobj "T" - IL_0036: newobj "InterpolationHandler..ctor(int, int, T)" - IL_003b: call "void E.M(T, int, InterpolationHandler)" - IL_0040: ret - } - """); - - verifier.VerifyIL("Porgram.Test2", """ + var expectedIL = """ { // Code size 26 (0x1a) .maxstack 5 @@ -21902,45 +21863,15 @@ .locals init (T V_0) IL_0014: call "void E.M(T, int, InterpolationHandler)" IL_0019: ret } - """); + """; - verifier.VerifyIL("Porgram.Test3", """ - { - // Code size 36 (0x24) - .maxstack 5 - .locals init (T& V_0) - IL_0000: ldsflda "T E.field" - IL_0005: stloc.0 - IL_0006: ldloc.0 - IL_0007: ldobj "T" - IL_000c: call "int Porgram.Increment()" - IL_0011: ldc.i4.0 - IL_0012: ldc.i4.0 - IL_0013: ldloc.0 - IL_0014: ldobj "T" - IL_0019: newobj "InterpolationHandler..ctor(int, int, T)" - IL_001e: call "void E.M(T, int, InterpolationHandler)" - IL_0023: ret - } - """); + verifier.VerifyIL("Porgram.Test1", expectedIL); - verifier.VerifyIL("Porgram.Test4", """ - { - // Code size 26 (0x1a) - .maxstack 5 - .locals init (T V_0) - IL_0000: ldsfld "T E.field" - IL_0005: stloc.0 - IL_0006: ldloc.0 - IL_0007: call "int Porgram.Increment()" - IL_000c: ldc.i4.0 - IL_000d: ldc.i4.0 - IL_000e: ldloc.0 - IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" - IL_0014: call "void E.M(T, int, InterpolationHandler)" - IL_0019: ret - } - """); + verifier.VerifyIL("Porgram.Test2", expectedIL); + + verifier.VerifyIL("Porgram.Test3", expectedIL); + + verifier.VerifyIL("Porgram.Test4", expectedIL); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); @@ -22012,31 +21943,11 @@ static void Test4() where T : struct } """; - var expectedOutput = "1033"; + var expectedOutput = "0033"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); - verifier.VerifyIL("Porgram.Test3", """ - { - // Code size 36 (0x24) - .maxstack 5 - .locals init (T& V_0) - IL_0000: ldsflda "T E.field" - IL_0005: stloc.0 - IL_0006: ldloc.0 - IL_0007: ldobj "T" - IL_000c: call "int Porgram.Increment()" - IL_0011: ldc.i4.0 - IL_0012: ldc.i4.0 - IL_0013: ldloc.0 - IL_0014: ldobj "T" - IL_0019: newobj "InterpolationHandler..ctor(int, int, T)" - IL_001e: call "void E.M(T, int, InterpolationHandler)" - IL_0023: ret - } - """); - - verifier.VerifyIL("Porgram.Test4", """ + var expectedIL = """ { // Code size 26 (0x1a) .maxstack 5 @@ -22052,7 +21963,11 @@ .locals init (T V_0) IL_0014: call "void E.M(T, int, InterpolationHandler)" IL_0019: ret } - """); + """; + + verifier.VerifyIL("Porgram.Test3", expectedIL); + + verifier.VerifyIL("Porgram.Test4", expectedIL); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); @@ -22141,42 +22056,7 @@ static void Test4() where T : class var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); - verifier.VerifyIL("Porgram.Test1", """ - { - // Code size 65 (0x41) - .maxstack 5 - .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) - IL_0000: ldsflda "T E.field" - IL_0005: stloc.2 - IL_0006: ldloca.s V_3 - IL_0008: initobj "T" - IL_000e: ldloc.3 - IL_000f: box "T" - IL_0014: brtrue.s IL_0021 - IL_0016: ldloc.2 - IL_0017: ldobj "T" - IL_001c: stloc.1 - IL_001d: ldloca.s V_1 - IL_001f: br.s IL_0022 - IL_0021: ldloc.2 - IL_0022: stloc.0 - IL_0023: ldloc.0 - IL_0024: ldobj "T" - IL_0029: call "int Porgram.Increment()" - IL_002e: ldc.i4.0 - IL_002f: ldc.i4.0 - IL_0030: ldloc.0 - IL_0031: ldobj "T" - IL_0036: newobj "InterpolationHandler..ctor(int, int, T)" - IL_003b: call "void E.M(T, int, InterpolationHandler)" - IL_0040: ret - } - """); - - verifier.VerifyIL("Porgram.Test2", """ + var expectedIL = """ { // Code size 26 (0x1a) .maxstack 5 @@ -22192,43 +22072,15 @@ .locals init (T V_0) IL_0014: call "void E.M(T, int, InterpolationHandler)" IL_0019: ret } - """); + """; - verifier.VerifyIL("Porgram.Test3", """ - { - // Code size 26 (0x1a) - .maxstack 5 - .locals init (T V_0) - IL_0000: ldsfld "T E.field" - IL_0005: stloc.0 - IL_0006: ldloc.0 - IL_0007: call "int Porgram.Increment()" - IL_000c: ldc.i4.0 - IL_000d: ldc.i4.0 - IL_000e: ldloc.0 - IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" - IL_0014: call "void E.M(T, int, InterpolationHandler)" - IL_0019: ret - } - """); + verifier.VerifyIL("Porgram.Test1", expectedIL); - verifier.VerifyIL("Porgram.Test4", """ - { - // Code size 26 (0x1a) - .maxstack 5 - .locals init (T V_0) - IL_0000: ldsfld "T E.field" - IL_0005: stloc.0 - IL_0006: ldloc.0 - IL_0007: call "int Porgram.Increment()" - IL_000c: ldc.i4.0 - IL_000d: ldc.i4.0 - IL_000e: ldloc.0 - IL_000f: newobj "InterpolationHandler..ctor(int, int, T)" - IL_0014: call "void E.M(T, int, InterpolationHandler)" - IL_0019: ret - } - """); + verifier.VerifyIL("Porgram.Test2", expectedIL); + + verifier.VerifyIL("Porgram.Test3", expectedIL); + + verifier.VerifyIL("Porgram.Test4", expectedIL); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 7f4bc99ff8eba..cee116de88a37 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -5629,34 +5629,41 @@ public static int Get1() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 18 (0x12) + // Code size 25 (0x19) .maxstack 2 + .locals init (int V_0) IL_0000: nop - IL_0001: ldsfld ""S1 Program.F"" + IL_0001: ldsflda ""S1 Program.F"" IL_0006: call ""int Program.Get1()"" - IL_000b: call ""void E.set_P1(S1, int)"" - IL_0010: nop - IL_0011: ret + IL_000b: stloc.0 + IL_000c: ldobj ""S1"" + IL_0011: ldloc.0 + IL_0012: call ""void E.set_P1(S1, int)"" + IL_0017: nop + IL_0018: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 19 (0x13) + // Code size 21 (0x15) .maxstack 2 + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""void E.set_P1(S1, int)"" - IL_0011: nop - IL_0012: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(S1, int)"" + IL_0013: nop + IL_0014: ret } "); @@ -6002,35 +6009,56 @@ static int Get1() """; var comp = CreateCompilation([src], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 19 (0x13) + // Code size 50 (0x32) .maxstack 2 + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""void E.set_P1(T, int)"" - IL_0011: nop - IL_0012: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 19 (0x13) + // Code size 21 (0x15) .maxstack 2 + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""void E.set_P1(T, int)"" - IL_0011: nop - IL_0012: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""T"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(T, int)"" + IL_0013: nop + IL_0014: ret } "); @@ -6329,15 +6357,33 @@ static int Get1() verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 19 (0x13) + // Code size 50 (0x32) .maxstack 2 + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""void E.set_P1(T, int)"" - IL_0011: nop - IL_0012: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); @@ -6549,24 +6595,27 @@ .maxstack 2 } [Fact] - public void PropertyAccess_CompoundAssignment_01() + [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] + public void PropertyAccess_Set_ReadonlyReceiver_040() { var src = """ +using System.Threading.Tasks; + static class E { - extension(S1 x) + extension(T x) { public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; + System.Console.Write(((S1)(object)x).F1); + Program.Increment(); return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -6575,87 +6624,102 @@ public int P1 struct S1 { public int F1; +} - public void Test() - { - this.P1 += Program.Get1(); - } +class Program +{ + public static readonly T F; } class Program { - public static S1 F; - - static void Main() + static async Task Main() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + Initialize(); + Test1(); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + Initialize(); + await Test3(); + System.Console.Write(Program.F.F1); } - static void Test() + static unsafe void Initialize() { - F.P1 += Get1(); + fixed (int* f1 = &Program.F.F1) + { + *f1 = 123; + } } - public static int Get1() + public static unsafe void Increment() { - Program.F.F1++; + fixed (int* f1 = &Program.F.F1) + { + (*f1)++; + } + } + + static void Test1() + { + Program.F.P1 = Get1(); + } + + static int Get1() + { + Increment(); return 1; } -} -"""; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + static async Task Test3() + { + Program.F.P1 = await Get1Async(); + } - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 37 (0x25) - .maxstack 3 - .locals init (int V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: ldobj ""S1"" - IL_000c: call ""int E.get_P1(S1)"" - IL_0011: call ""int Program.Get1()"" - IL_0016: add - IL_0017: stloc.0 - IL_0018: ldobj ""S1"" - IL_001d: ldloc.0 - IL_001e: call ""void E.set_P1(S1, int)"" - IL_0023: nop - IL_0024: ret + static async Task Get1Async() + { + Increment(); + await Task.Yield(); + return 1; + } } -"); +"""; - verifier.VerifyIL("S1.Test", + var comp = CreateCompilation([src], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", @" { - // Code size 33 (0x21) + // Code size 54 (0x36) .maxstack 2 - .locals init (int V_0) + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""int E.get_P1(S1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: stloc.0 - IL_0013: ldarg.0 - IL_0014: ldobj ""S1"" - IL_0019: ldloc.0 - IL_001a: call ""void E.set_P1(S1, int)"" - IL_001f: nop - IL_0020: ret + IL_0001: ldsflda ""T Program.F"" + IL_0006: stloc.1 + IL_0007: ldloca.s V_3 + IL_0009: initobj ""T"" + IL_000f: ldloc.3 + IL_0010: box ""T"" + IL_0015: brtrue.s IL_0022 + IL_0017: ldloc.1 + IL_0018: ldobj ""T"" + IL_001d: stloc.0 + IL_001e: ldloca.s V_0 + IL_0020: br.s IL_0023 + IL_0022: ldloc.1 + IL_0023: call ""int Program.Get1()"" + IL_0028: stloc.2 + IL_0029: ldobj ""T"" + IL_002e: ldloc.2 + IL_002f: call ""void E.set_P1(T, int)"" + IL_0034: nop + IL_0035: ret } "); } @@ -6664,24 +6728,24 @@ .locals init (int V_0) [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] - public void PropertyAccess_CompoundAssignment_02(string refKind) + public void PropertyAccess_Set_ReadonlyReceiver_041(string refKind) { var src = $$$""" static class E { - extension({{{refKind}}} S1 x) + extension(T x) { public int P1 { get { - System.Console.Write(x.F1); - Program.F.F1++; + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; return 0; } set { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); } } } @@ -6690,130 +6754,434 @@ public int P1 struct S1 { public int F1; +} - public void Test() - { - this.P1 += Program.Get1(); - } +class Program +{ + public static T F; } class Program { - public static S1 F; - static void Main() { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); + Program.F = new S1 { F1 = 123 }; + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); + System.Console.Write(Program.F.F1); } - static void Test() + static void Test1({{{refKind}}} T f) { - F.P1 += Get1(); + f.P1 = Get1(); } - public static int Get1() + static int Get1() { - Program.F.F1++; + Program.F.F1++; return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", + verifier.VerifyIL($"Program.Test1({refKind} T)", @" { - // Code size 25 (0x19) - .maxstack 3 - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: dup - IL_0007: call ""int E.get_P1(" + refKind + @" S1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0017: nop - IL_0018: ret + // Code size 50 (0x32) + .maxstack 2 + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); + } - verifier.VerifyIL("S1.Test", + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_Set_ReadonlyReceiver_061(string refKind) + { + var src = $$$""" +static class E +{ + extension(T x) + { + public int P1 + { + get + { + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(((C1)(object)x).F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static void Main() + { + Program.F = new C1 { F1 = 123 }; + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); + System.Console.Write(Program.F.F1); + } + + static void Test1({{{refKind}}} T f) + { + f.P1 = Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation([src], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123124").VerifyDiagnostics(); + + verifier.VerifyIL($"Program.Test1({refKind} T)", @" { - // Code size 21 (0x15) - .maxstack 3 + // Code size 50 (0x32) + .maxstack 2 + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldarg.0 - IL_0003: call ""int E.get_P1(" + refKind + @" S1)"" - IL_0008: call ""int Program.Get1()"" - IL_000d: add - IL_000e: call ""void E.set_P1(" + refKind + @" S1, int)"" - IL_0013: nop - IL_0014: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""void E.set_P1(T, int)"" + IL_0030: nop + IL_0031: ret } "); + } - var src2 = $$$""" + [Fact] + public void PropertyAccess_CompoundAssignment_01() + { + var src = """ static class E { - extension({{{refKind}}} S1 x) + extension(S1 x) { - public int P1 { get => 0; set {} } + public int P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } } } -struct S1; +struct S1 +{ + public int F1; + + public void Test() + { + this.P1 += Program.Get1(); + } +} class Program { + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + static void Test() { - default(S1).P1 += 1; + F.P1 += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; } } """; - var comp2 = CreateCompilation(src2); - switch (refKind) - { - case "ref": - comp2.VerifyDiagnostics( - // (15,9): error CS1510: A ref or out value must be an assignable variable - // default(S1).P1 += 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) - ); - break; - case "ref readonly": - comp2.VerifyDiagnostics( - // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter - // default(S1).P1 += 1; - Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), - // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) - ); - break; - case "in": - comp2.VerifyDiagnostics( - // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // default(S1).P1 += 1; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) - ); - break; - default: - throw ExceptionUtilities.UnexpectedValue(refKind); - } + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 37 (0x25) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: ldobj ""S1"" + IL_000c: call ""int E.get_P1(S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: add + IL_0017: stloc.0 + IL_0018: ldobj ""S1"" + IL_001d: ldloc.0 + IL_001e: call ""void E.set_P1(S1, int)"" + IL_0023: nop + IL_0024: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldobj ""S1"" + IL_0007: call ""int E.get_P1(S1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: stloc.0 + IL_0013: ldarg.0 + IL_0014: ldobj ""S1"" + IL_0019: ldloc.0 + IL_001a: call ""void E.set_P1(S1, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_CompoundAssignment_02(string refKind) + { + var src = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 + { + get + { + System.Console.Write(x.F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +struct S1 +{ + public int F1; + + public void Test() + { + this.P1 += Program.Get1(); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F.P1 += Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 25 (0x19) + .maxstack 3 + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: dup + IL_0007: call ""int E.get_P1(" + refKind + @" S1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 21 (0x15) + .maxstack 3 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.0 + IL_0003: call ""int E.get_P1(" + refKind + @" S1)"" + IL_0008: call ""int Program.Get1()"" + IL_000d: add + IL_000e: call ""void E.set_P1(" + refKind + @" S1, int)"" + IL_0013: nop + IL_0014: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension({{{refKind}}} S1 x) + { + public int P1 { get => 0; set {} } + } +} + +struct S1; + +class Program +{ + static void Test() + { + default(S1).P1 += 1; + } +} +"""; + + var comp2 = CreateCompilation(src2); + switch (refKind) + { + case "ref": + comp2.VerifyDiagnostics( + // (15,9): error CS1510: A ref or out value must be an assignable variable + // default(S1).P1 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(15, 9) + ); + break; + case "ref readonly": + comp2.VerifyDiagnostics( + // (15,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // default(S1).P1 += 1; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(15, 9), + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + case "in": + comp2.VerifyDiagnostics( + // (15,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // default(S1).P1 += 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1).P1").WithLocation(15, 9) + ); + break; + default: + throw ExceptionUtilities.UnexpectedValue(refKind); + } } [Fact] @@ -6822,76 +7190,374 @@ public void PropertyAccess_CompoundAssignment_03() var src = """ static class E { - extension(C1 x) + extension(C1 x) + { + public int P1 + { + get + { + System.Console.Write(x.F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + public static C1 F; + + static void Main() + { + F = new C1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + } + + static void Test() + { + F.P1 += Get1(); + } + + static int Get1() + { + Program.F = new C1 { F1 = Program.F.F1 + 1 }; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 25 (0x19) + .maxstack 3 + IL_0000: nop + IL_0001: ldsfld ""C1 Program.F"" + IL_0006: dup + IL_0007: call ""int E.get_P1(C1)"" + IL_000c: call ""int Program.Get1()"" + IL_0011: add + IL_0012: call ""void E.set_P1(C1, int)"" + IL_0017: nop + IL_0018: ret +} +"); + } + + [Fact] + public void PropertyAccess_CompoundAssignment_04() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(T x) + { + public int P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test1(ref T f) + { + f.P1 += Get1(); + } + + static void Test2(ref T f) where T : struct + { + f.P1 += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1(ref T)", +@" +{ + // Code size 62 (0x3e) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret +} +"); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 33 (0x21) + .maxstack 3 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: ldobj ""T"" + IL_0008: call ""int E.get_P1(T)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldobj ""T"" + IL_0019: ldloc.0 + IL_001a: call ""void E.set_P1(T, int)"" + IL_001f: nop + IL_0020: ret +} +"); + } + + [Fact] + public void PropertyAccess_CompoundAssignment_05() + { + var src = """ +using System.Threading.Tasks; + +static class E +{ + extension(ref T x) where T : struct + { + public int P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + +class Program +{ + static async Task Main() + { + Program.F = new S1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); + + System.Console.Write(":"); + + Program.F = new S1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); + } + + static void Test2(ref T f) where T : struct + { + f.P1 += Get1(); + } + + static int Get1() + { + Program.F.F1++; + return 1; + } + + static async Task Test3() where T : struct + { + Program.F.P1 += await Get1Async(); + } + + static async Task Get1Async() + { + Program.F.F1++; + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test2(ref T)", +@" +{ + // Code size 21 (0x15) + .maxstack 3 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: dup + IL_0003: call ""int E.get_P1(ref T)"" + IL_0008: call ""int Program.Get1()"" + IL_000d: add + IL_000e: call ""void E.set_P1(ref T, int)"" + IL_0013: nop + IL_0014: ret +} +"); + + var src2 = """ +static class E +{ + extension(ref T x) where T : struct { - public int P1 - { - get - { - System.Console.Write(x.F1); - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return 0; - } - set - { - System.Console.Write(x.F1); - } - } + public int P1 { get => 0; set {} } } } -class C1 -{ - public int F1; -} - class Program { - public static C1 F; - - static void Main() + static void Test() where T : struct { - F = new C1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); + default(T).P1 += 1; } +} - static void Test() +namespace NS1 +{ + static class E { - F.P1 += Get1(); + extension(in T x) where T : struct + { + } } +} - static int Get1() +namespace NS2 +{ + static class E { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - return 1; + extension(ref readonly T x) where T : struct + { + } } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test", -@" -{ - // Code size 25 (0x19) - .maxstack 3 - IL_0000: nop - IL_0001: ldsfld ""C1 Program.F"" - IL_0006: dup - IL_0007: call ""int E.get_P1(C1)"" - IL_000c: call ""int Program.Get1()"" - IL_0011: add - IL_0012: call ""void E.set_P1(C1, int)"" - IL_0017: nop - IL_0018: ret -} -"); + var comp2 = CreateCompilation(src2); + comp2.VerifyDiagnostics( + // (13,9): error CS1510: A ref or out value must be an assignable variable + // default(T).P1 += 1; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), + // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(in T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), + // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. + // extension(ref readonly T x) where T : struct + Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) + ); } [Fact] - public void PropertyAccess_CompoundAssignment_04() + public void PropertyAccess_CompoundAssignment_06() { var src = """ using System.Threading.Tasks; @@ -6904,19 +7570,19 @@ public int P1 { get { - System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; + System.Console.Write(((C1)(object)x).F1); + Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } set { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(((C1)(object)x).F1); } } } } -struct S1 +class C1 { public int F1; } @@ -6930,21 +7596,21 @@ class Program { static async Task Main() { - Program.F = new S1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + Test1(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + Test2(ref Program.F); + System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; - await Test3(); - System.Console.Write(Program.F.F1); + Program.F = new C1 { F1 = 123 }; + await Test3(); + System.Console.Write(Program.F.F1); } static void Test1(ref T f) @@ -6952,14 +7618,14 @@ static void Test1(ref T f) f.P1 += Get1(); } - static void Test2(ref T f) where T : struct + static void Test2(ref T f) where T : class { f.P1 += Get1(); } static int Get1() { - Program.F.F1++; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } @@ -6970,7 +7636,7 @@ static async Task Test3() static async Task Get1Async() { - Program.F.F1++; + Program.F = new C1 { F1 = Program.F.F1 + 1 }; await Task.Yield(); return 1; } @@ -6978,7 +7644,7 @@ static async Task Get1Async() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125:123125125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" @@ -7020,42 +7686,38 @@ .locals init (T V_0, verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 33 (0x21) + // Code size 26 (0x1a) .maxstack 3 - .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: dup - IL_0003: ldobj ""T"" + IL_0002: ldobj ""T"" + IL_0007: dup IL_0008: call ""int E.get_P1(T)"" IL_000d: call ""int Program.Get1()"" IL_0012: add - IL_0013: stloc.0 - IL_0014: ldobj ""T"" - IL_0019: ldloc.0 - IL_001a: call ""void E.set_P1(T, int)"" - IL_001f: nop - IL_0020: ret + IL_0013: call ""void E.set_P1(T, int)"" + IL_0018: nop + IL_0019: ret } "); } [Fact] - public void PropertyAccess_CompoundAssignment_05() + public void PropertyAccess_CompoundAssignment_ReadonlyReceiver_040() { var src = """ using System.Threading.Tasks; static class E { - extension(ref T x) where T : struct + extension(T x) { public int P1 { get { System.Console.Write(((S1)(object)x).F1); - Program.F.F1++; + Program.Increment(); return 0; } set @@ -7073,127 +7735,213 @@ struct S1 class Program { - public static T F; + public static readonly T F; } class Program { static async Task Main() { - Program.F = new S1 { F1 = 123 }; - Test2(ref Program.F); + Initialize(); + Test1(); System.Console.Write(Program.F.F1); System.Console.Write(":"); - Program.F = new S1 { F1 = 123 }; + Initialize(); await Test3(); System.Console.Write(Program.F.F1); } - static void Test2(ref T f) where T : struct + static unsafe void Initialize() { - f.P1 += Get1(); + fixed (int* f1 = &Program.F.F1) + { + *f1 = 123; + } + } + + public static unsafe void Increment() + { + fixed (int* f1 = &Program.F.F1) + { + (*f1)++; + } + } + + static void Test1() + { + Program.F.P1 += Get1(); } static int Get1() { - Program.F.F1++; + Increment(); return 1; } - static async Task Test3() where T : struct + static async Task Test3() { Program.F.P1 += await Get1Async(); } static async Task Get1Async() { - Program.F.F1++; + Increment(); await Task.Yield(); return 1; } } """; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125").VerifyDiagnostics(); + var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125", verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2(ref T)", + verifier.VerifyIL("Program.Test1()", @" { - // Code size 21 (0x15) + // Code size 66 (0x42) .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) IL_0000: nop - IL_0001: ldarg.0 - IL_0002: dup - IL_0003: call ""int E.get_P1(ref T)"" - IL_0008: call ""int Program.Get1()"" - IL_000d: add - IL_000e: call ""void E.set_P1(ref T, int)"" - IL_0013: nop - IL_0014: ret + IL_0001: ldsflda ""T Program.F"" + IL_0006: stloc.1 + IL_0007: ldloca.s V_2 + IL_0009: initobj ""T"" + IL_000f: ldloc.2 + IL_0010: box ""T"" + IL_0015: brtrue.s IL_0022 + IL_0017: ldloc.1 + IL_0018: ldobj ""T"" + IL_001d: stloc.0 + IL_001e: ldloca.s V_0 + IL_0020: br.s IL_0023 + IL_0022: ldloc.1 + IL_0023: dup + IL_0024: ldobj ""T"" + IL_0029: call ""int E.get_P1(T)"" + IL_002e: call ""int Program.Get1()"" + IL_0033: add + IL_0034: stloc.3 + IL_0035: ldobj ""T"" + IL_003a: ldloc.3 + IL_003b: call ""void E.set_P1(T, int)"" + IL_0040: nop + IL_0041: ret } "); + } - var src2 = """ + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_CompoundAssignment_ReadonlyReceiver_041(string refKind) + { + var src = $$$""" static class E { - extension(ref T x) where T : struct + extension(T x) { - public int P1 { get => 0; set {} } + public int P1 + { + get + { + System.Console.Write(((S1)(object)x).F1); + Program.F.F1++; + return 0; + } + set + { + System.Console.Write(((S1)(object)x).F1); + } + } } } +struct S1 +{ + public int F1; +} + +class Program +{ + public static T F; +} + class Program { - static void Test() where T : struct + static void Main() { - default(T).P1 += 1; + Program.F = new S1 { F1 = 123 }; + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); + System.Console.Write(Program.F.F1); } -} -namespace NS1 -{ - static class E + static void Test1({{{refKind}}} T f) { - extension(in T x) where T : struct - { - } + f.P1 += Get1(); } -} -namespace NS2 -{ - static class E + static int Get1() { - extension(ref readonly T x) where T : struct - { - } + Program.F.F1++; + return 1; } } -"""; - - var comp2 = CreateCompilation(src2); - comp2.VerifyDiagnostics( - // (13,9): error CS1510: A ref or out value must be an assignable variable - // default(T).P1 += 1; - Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(13, 9), - // (21,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(in T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(21, 25), - // (31,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. - // extension(ref readonly T x) where T : struct - Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(31, 35) - ); +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123125125").VerifyDiagnostics(); + + verifier.VerifyIL($"Program.Test1({refKind} T)", +@" +{ + // Code size 62 (0x3e) + .maxstack 3 + .locals init (T V_0, + T& V_1, + T V_2, + int V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_2 + IL_0005: initobj ""T"" + IL_000b: ldloc.2 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: dup + IL_0020: ldobj ""T"" + IL_0025: call ""int E.get_P1(T)"" + IL_002a: call ""int Program.Get1()"" + IL_002f: add + IL_0030: stloc.3 + IL_0031: ldobj ""T"" + IL_0036: ldloc.3 + IL_0037: call ""void E.set_P1(T, int)"" + IL_003c: nop + IL_003d: ret +} +"); } - [Fact] - public void PropertyAccess_CompoundAssignment_06() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PropertyAccess_CompoundAssignment_ReadonlyReceiver_061(string refKind) { - var src = """ -using System.Threading.Tasks; - + var src = $$$""" static class E { extension(T x) @@ -7226,31 +7974,14 @@ class Program class Program { - static async Task Main() + static void Main() { Program.F = new C1 { F1 = 123 }; - Test1(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - Test2(ref Program.F); - System.Console.Write(Program.F.F1); - - System.Console.Write(":"); - - Program.F = new C1 { F1 = 123 }; - await Test3(); + Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program.F); System.Console.Write(Program.F.F1); } - static void Test1(ref T f) - { - f.P1 += Get1(); - } - - static void Test2(ref T f) where T : class + static void Test1({{{refKind}}} T f) { f.P1 += Get1(); } @@ -7260,25 +7991,13 @@ static int Get1() Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } - - static async Task Test3() - { - Program.F.P1 += await Get1Async(); - } - - static async Task Get1Async() - { - Program.F = new C1 { F1 = Program.F.F1 + 1 }; - await Task.Yield(); - return 1; - } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123125").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test1(ref T)", + verifier.VerifyIL($"Program.Test1({refKind} T)", @" { // Code size 62 (0x3e) @@ -7313,24 +8032,6 @@ .locals init (T V_0, IL_003c: nop IL_003d: ret } -"); - - verifier.VerifyIL("Program.Test2(ref T)", -@" -{ - // Code size 26 (0x1a) - .maxstack 3 - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: dup - IL_0008: call ""int E.get_P1(T)"" - IL_000d: call ""int Program.Get1()"" - IL_0012: add - IL_0013: call ""void E.set_P1(T, int)"" - IL_0018: nop - IL_0019: ret -} "); } @@ -11963,7 +12664,6 @@ .locals init (T V_0) } [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] public void IndexerAccess_CompoundAssignment_07() { var src = """ @@ -11995,16 +12695,13 @@ struct S1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Test1(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } static T GetT() => (T)(object)new S1 { F1 = 123 }; @@ -12019,68 +12716,46 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[0] += await Get1Async(); - //} + static async Task Test3() + { + GetT()[0] += await Get1Async(); + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 73 (0x49) + // Code size 29 (0x1d) .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - T V_3, - int V_4) + .locals init (int V_0) IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.2 - IL_0007: ldloca.s V_2 - IL_0009: stloc.1 - IL_000a: ldloca.s V_3 - IL_000c: initobj ""T"" - IL_0012: ldloc.3 - IL_0013: box ""T"" - IL_0018: brtrue.s IL_0025 - IL_001a: ldloc.1 - IL_001b: ldobj ""T"" - IL_0020: stloc.0 - IL_0021: ldloca.s V_0 - IL_0023: br.s IL_0026 - IL_0025: ldloc.1 - IL_0026: dup - IL_0027: ldobj ""T"" - IL_002c: ldc.i4.0 - IL_002d: call ""int E.get_Item(T, int)"" - IL_0032: call ""int Program.Get1()"" - IL_0037: add - IL_0038: stloc.s V_4 - IL_003a: ldobj ""T"" - IL_003f: ldc.i4.0 - IL_0040: ldloc.s V_4 - IL_0042: call ""void E.set_Item(T, int, int)"" - IL_0047: nop - IL_0048: ret + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: call ""int E.get_Item(T, int)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldc.i4.0 + IL_0015: ldloc.0 + IL_0016: call ""void E.set_Item(T, int, int)"" + IL_001b: nop + IL_001c: ret } "); } [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] public void IndexerAccess_CompoundAssignment_08() { var src = """ @@ -12112,8 +12787,6 @@ class C1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Test1(); @@ -12122,10 +12795,9 @@ static async Task Main() Test2(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } static T GetT() => (T)(object)new C1 { F1 = 123 }; @@ -12145,62 +12817,41 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[0] += await Get1Async(); - //} + static async Task Test3() + { + GetT()[0] += await Get1Async(); + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" { - // Code size 73 (0x49) + // Code size 29 (0x1d) .maxstack 3 - .locals init (T V_0, - T& V_1, - T V_2, - T V_3, - int V_4) + .locals init (int V_0) IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.2 - IL_0007: ldloca.s V_2 - IL_0009: stloc.1 - IL_000a: ldloca.s V_3 - IL_000c: initobj ""T"" - IL_0012: ldloc.3 - IL_0013: box ""T"" - IL_0018: brtrue.s IL_0025 - IL_001a: ldloc.1 - IL_001b: ldobj ""T"" - IL_0020: stloc.0 - IL_0021: ldloca.s V_0 - IL_0023: br.s IL_0026 - IL_0025: ldloc.1 - IL_0026: dup - IL_0027: ldobj ""T"" - IL_002c: ldc.i4.0 - IL_002d: call ""int E.get_Item(T, int)"" - IL_0032: call ""int Program.Get1()"" - IL_0037: add - IL_0038: stloc.s V_4 - IL_003a: ldobj ""T"" - IL_003f: ldc.i4.0 - IL_0040: ldloc.s V_4 - IL_0042: call ""void E.set_Item(T, int, int)"" - IL_0047: nop - IL_0048: ret + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: call ""int E.get_Item(T, int)"" + IL_000d: call ""int Program.Get1()"" + IL_0012: add + IL_0013: stloc.0 + IL_0014: ldc.i4.0 + IL_0015: ldloc.0 + IL_0016: call ""void E.set_Item(T, int, int)"" + IL_001b: nop + IL_001c: ret } "); @@ -13445,57 +14096,36 @@ static int Get1() verifier.VerifyIL("Program.Test1()", @" { - // Code size 102 (0x66) + // Code size 48 (0x30) .maxstack 4 - .locals init (T& V_0, - T V_1, - T& V_2, - int V_3, - InterpolationHandler V_4, - T V_5, - T V_6, - int V_7) + .locals init (T V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.s V_5 - IL_0008: ldloca.s V_5 - IL_000a: stloc.2 - IL_000b: ldloca.s V_6 - IL_000d: initobj ""T"" - IL_0013: ldloc.s V_6 - IL_0015: box ""T"" - IL_001a: brtrue.s IL_0027 - IL_001c: ldloc.2 - IL_001d: ldobj ""T"" - IL_0022: stloc.1 - IL_0023: ldloca.s V_1 - IL_0025: br.s IL_0028 + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_001e: call ""int Program.Get1()"" + IL_0023: add + IL_0024: stloc.3 + IL_0025: ldloc.0 + IL_0026: ldloc.1 IL_0027: ldloc.2 - IL_0028: stloc.0 - IL_0029: call ""int Program.Get1()"" - IL_002e: stloc.3 - IL_002f: ldc.i4.0 - IL_0030: ldc.i4.0 - IL_0031: ldloc.0 - IL_0032: ldobj ""T"" - IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_003c: stloc.s V_4 - IL_003e: ldloc.0 - IL_003f: ldobj ""T"" - IL_0044: ldloc.3 - IL_0045: ldloc.s V_4 - IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_004c: call ""int Program.Get1()"" - IL_0051: add - IL_0052: stloc.s V_7 - IL_0054: ldloc.0 - IL_0055: ldobj ""T"" - IL_005a: ldloc.3 - IL_005b: ldloc.s V_4 - IL_005d: ldloc.s V_7 - IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0064: nop - IL_0065: ret + IL_0028: ldloc.3 + IL_0029: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002e: nop + IL_002f: ret } "); } @@ -13598,57 +14228,36 @@ static int Get1() verifier.VerifyIL("Program.Test1()", @" { - // Code size 102 (0x66) + // Code size 48 (0x30) .maxstack 4 - .locals init (T& V_0, - T V_1, - T& V_2, - int V_3, - InterpolationHandler V_4, - T V_5, - T V_6, - int V_7) + .locals init (T V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.s V_5 - IL_0008: ldloca.s V_5 - IL_000a: stloc.2 - IL_000b: ldloca.s V_6 - IL_000d: initobj ""T"" - IL_0013: ldloc.s V_6 - IL_0015: box ""T"" - IL_001a: brtrue.s IL_0027 - IL_001c: ldloc.2 - IL_001d: ldobj ""T"" - IL_0022: stloc.1 - IL_0023: ldloca.s V_1 - IL_0025: br.s IL_0028 + IL_0006: stloc.0 + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: call ""int E.get_Item(T, int, InterpolationHandler)"" + IL_001e: call ""int Program.Get1()"" + IL_0023: add + IL_0024: stloc.3 + IL_0025: ldloc.0 + IL_0026: ldloc.1 IL_0027: ldloc.2 - IL_0028: stloc.0 - IL_0029: call ""int Program.Get1()"" - IL_002e: stloc.3 - IL_002f: ldc.i4.0 - IL_0030: ldc.i4.0 - IL_0031: ldloc.0 - IL_0032: ldobj ""T"" - IL_0037: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_003c: stloc.s V_4 - IL_003e: ldloc.0 - IL_003f: ldobj ""T"" - IL_0044: ldloc.3 - IL_0045: ldloc.s V_4 - IL_0047: call ""int E.get_Item(T, int, InterpolationHandler)"" - IL_004c: call ""int Program.Get1()"" - IL_0051: add - IL_0052: stloc.s V_7 - IL_0054: ldloc.0 - IL_0055: ldobj ""T"" - IL_005a: ldloc.3 - IL_005b: ldloc.s V_4 - IL_005d: ldloc.s V_7 - IL_005f: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0064: nop - IL_0065: ret + IL_0028: ldloc.3 + IL_0029: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002e: nop + IL_002f: ret } "); @@ -13762,53 +14371,71 @@ public static int Get1() """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 43 (0x2b) - .maxstack 5 - .locals init (S1& V_0) + // Code size 49 (0x31) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""int Program.Get1()"" - IL_0012: ldc.i4.0 - IL_0013: ldc.i4.0 - IL_0014: ldloc.0 - IL_0015: ldobj ""S1"" - IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001f: call ""int Program.Get1()"" - IL_0024: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" - IL_0029: nop - IL_002a: ret + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: ldobj ""S1"" + IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001a: stloc.2 + IL_001b: call ""int Program.Get1()"" + IL_0020: stloc.3 + IL_0021: ldloc.0 + IL_0022: ldobj ""S1"" + IL_0027: ldloc.1 + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_002f: nop + IL_0030: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (S1& V_0) + // Code size 45 (0x2d) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""S1"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""S1"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" - IL_0025: nop - IL_0026: ret + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""S1"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""S1"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""void E.set_Item(S1, int, InterpolationHandler, int)"" + IL_002b: nop + IL_002c: ret } "); @@ -14216,68 +14843,86 @@ static int Get1() """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 68 (0x44) - .maxstack 5 + // Code size 79 (0x4f) + .maxstack 4 .locals init (T& V_0, T V_1, T& V_2, - T V_3) + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 - IL_0003: ldloca.s V_3 + IL_0003: ldloca.s V_6 IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0042: nop - IL_0043: ret + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_004d: nop + IL_004e: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (T& V_0) + // Code size 45 (0x2d) + .maxstack 4 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""T"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0025: nop - IL_0026: ret + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""T"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""T"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_002b: nop + IL_002c: ret } "); @@ -14632,39 +15277,48 @@ static int Get1() verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 68 (0x44) - .maxstack 5 + // Code size 79 (0x4f) + .maxstack 4 .locals init (T& V_0, T V_1, T& V_2, - T V_3) + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 - IL_0003: ldloca.s V_3 + IL_0003: ldloca.s V_6 IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""void E.set_Item(T, int, InterpolationHandler, int)"" - IL_0042: nop - IL_0043: ret + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""void E.set_Item(T, int, InterpolationHandler, int)"" + IL_004d: nop + IL_004e: ret } "); @@ -15005,53 +15659,71 @@ public static int Get1() """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 43 (0x2b) - .maxstack 5 - .locals init (S1& V_0) + // Code size 49 (0x31) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: ldsflda ""S1 Program.F"" IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: ldobj ""S1"" - IL_000d: call ""int Program.Get1()"" - IL_0012: ldc.i4.0 - IL_0013: ldc.i4.0 - IL_0014: ldloc.0 - IL_0015: ldobj ""S1"" - IL_001a: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001f: call ""int Program.Get1()"" - IL_0024: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" - IL_0029: pop - IL_002a: ret + IL_0007: call ""int Program.Get1()"" + IL_000c: stloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: ldobj ""S1"" + IL_0015: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_001a: stloc.2 + IL_001b: call ""int Program.Get1()"" + IL_0020: stloc.3 + IL_0021: ldloc.0 + IL_0022: ldobj ""S1"" + IL_0027: ldloc.1 + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_002f: pop + IL_0030: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (S1& V_0) + // Code size 45 (0x2d) + .maxstack 4 + .locals init (S1& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""S1"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""S1"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, S1)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" - IL_0025: pop - IL_0026: ret + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""S1"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, S1)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""S1"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""int E.get_Item(S1, int, InterpolationHandler, int)"" + IL_002b: pop + IL_002c: ret } "); } @@ -15404,68 +16076,86 @@ static int Get1() """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124123126:124123126").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126:124126126").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 68 (0x44) - .maxstack 5 + // Code size 79 (0x4f) + .maxstack 4 .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 - IL_0003: ldloca.s V_3 + IL_0003: ldloca.s V_6 IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_0042: pop - IL_0043: ret + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_004d: pop + IL_004e: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 39 (0x27) - .maxstack 5 - .locals init (T& V_0) + // Code size 45 (0x2d) + .maxstack 4 + .locals init (T& V_0, + int V_1, + InterpolationHandler V_2, + int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 - IL_0003: ldloc.0 - IL_0004: ldobj ""T"" - IL_0009: call ""int Program.Get1()"" - IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloc.0 - IL_0011: ldobj ""T"" - IL_0016: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_001b: call ""int Program.Get1()"" - IL_0020: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_0025: pop - IL_0026: ret + IL_0003: call ""int Program.Get1()"" + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: ldobj ""T"" + IL_0011: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0016: stloc.2 + IL_0017: call ""int Program.Get1()"" + IL_001c: stloc.3 + IL_001d: ldloc.0 + IL_001e: ldobj ""T"" + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_002b: pop + IL_002c: ret } "); } @@ -15747,39 +16437,48 @@ static int Get1() verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 68 (0x44) - .maxstack 5 + // Code size 79 (0x4f) + .maxstack 4 .locals init (T& V_0, - T V_1, - T& V_2, - T V_3) + T V_1, + T& V_2, + int V_3, + InterpolationHandler V_4, + int V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 - IL_0003: ldloca.s V_3 + IL_0003: ldloca.s V_6 IL_0005: initobj ""T"" - IL_000b: ldloc.3 - IL_000c: box ""T"" - IL_0011: brtrue.s IL_001e - IL_0013: ldloc.2 - IL_0014: ldobj ""T"" - IL_0019: stloc.1 - IL_001a: ldloca.s V_1 - IL_001c: br.s IL_001f - IL_001e: ldloc.2 - IL_001f: stloc.0 - IL_0020: ldloc.0 - IL_0021: ldobj ""T"" - IL_0026: call ""int Program.Get1()"" - IL_002b: ldc.i4.0 - IL_002c: ldc.i4.0 - IL_002d: ldloc.0 - IL_002e: ldobj ""T"" - IL_0033: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0038: call ""int Program.Get1()"" - IL_003d: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_0042: pop - IL_0043: ret + IL_000b: ldloc.s V_6 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.2 + IL_0015: ldobj ""T"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: br.s IL_0020 + IL_001f: ldloc.2 + IL_0020: stloc.0 + IL_0021: call ""int Program.Get1()"" + IL_0026: stloc.3 + IL_0027: ldc.i4.0 + IL_0028: ldc.i4.0 + IL_0029: ldloc.0 + IL_002a: ldobj ""T"" + IL_002f: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0034: stloc.s V_4 + IL_0036: call ""int Program.Get1()"" + IL_003b: stloc.s V_5 + IL_003d: ldloc.0 + IL_003e: ldobj ""T"" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: ldloc.s V_5 + IL_0048: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_004d: pop + IL_004e: ret } "); @@ -15985,12 +16684,230 @@ public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_03() { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, C1 x) + { + System.Console.Write(x.F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(C1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} + +class C1 +{ + public int F1; +} + +class Program +{ + static void Main() + { + Test(); + } + + static void Test() + { + _ = GetC1()[Get1(), $"", Get1()]; + } + + static C1 GetC1() => new C1 { F1 = 123 }; + + static int Get1() + { + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (C1 V_0) + IL_0000: nop + IL_0001: call ""C1 Program.GetC1()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_04() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, TR x) + { + System.Console.Write(((S1)(object)x).F1); + } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + + +static class E +{ + extension(T x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(((S1)(object)x).F1); + return 0; + } + } + } +} + +struct S1 +{ + public int F1; +} + +class Program +{ + static async Task Main() + { + Test1(); + + System.Console.Write(":"); + + Test2(); + + System.Console.Write(":"); + + await Test3(); + } + + static T GetT() => (T)(object)new S1 { F1 = 123 }; + + static void Test1() + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static void Test2() where T : struct + { + _ = GetT()[Get1(), $"", Get1()]; + } + + static int Get1() + { + return 1; + } + + static async Task Test3() + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } +} +"""; + + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + + verifier.VerifyIL("Program.Test2()", +@" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (T V_0) + IL_0000: nop + IL_0001: call ""T Program.GetT()"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret +} +"); + } + + [Fact] + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_05() + { + var src = """ +using System.Threading.Tasks; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, C1 x) + public InterpolationHandler(int literalLength, int formattedCount, ref TR x) { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); + x = (TR)(object)new S1 { F1 = ((S1)(object)x).F1 + 1 }; } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; @@ -15998,73 +16915,88 @@ public void AppendLiteral(string value) { } static class E { - extension(C1 x) + extension(ref T x) where T : struct { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(x.F1); + System.Console.Write(((S1)(object)x).F1); return 0; } } } } -class C1 +struct S1 { public int F1; } class Program { - static void Main() + static async Task Main() { - Test(); + Test2(); + + System.Console.Write(":"); + + await Test3(); } - static void Test() + static void Test2() where T : struct { - _ = GetC1()[Get1(), $"", Get1()]; + _ = GetT()[Get1(), $"", Get1()]; } - static C1 GetC1() => new C1 { F1 = 123 }; + static T GetT() => (T)(object)new S1 { F1 = 123 }; static int Get1() { return 1; } + + static async Task Test3() where T : struct + { + _ = GetT()[Get1(), $"", await Get1Async()]; + } + + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test", + verifier.VerifyIL("Program.Test2()", @" { - // Code size 33 (0x21) + // Code size 35 (0x23) .maxstack 5 - .locals init (C1 V_0) + .locals init (T V_0) IL_0000: nop - IL_0001: call ""C1 Program.GetC1()"" + IL_0001: call ""T Program.GetT()"" IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""int Program.Get1()"" IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, C1)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(C1, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret + IL_000f: ldc.i4.0 + IL_0010: ldloca.s V_0 + IL_0012: newobj ""InterpolationHandler..ctor(int, int, ref T)"" + IL_0017: call ""int Program.Get1()"" + IL_001c: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" + IL_0021: pop + IL_0022: ret } "); } [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_04() + public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_06() { var src = """ using System.Threading.Tasks; @@ -16075,13 +17007,12 @@ struct InterpolationHandler public InterpolationHandler(int literalLength, int formattedCount, TR x) { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(((C1)(object)x).F1); } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; } - static class E { extension(T x) @@ -16090,14 +17021,14 @@ static class E { get { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(((C1)(object)x).F1); return 0; } } } } -struct S1 +class C1 { public int F1; } @@ -16106,25 +17037,25 @@ class Program { static async Task Main() { - Test1(); + Test1(); System.Console.Write(":"); - Test2(); + Test2(); System.Console.Write(":"); - await Test3(); + await Test3(); } - static T GetT() => (T)(object)new S1 { F1 = 123 }; + static T GetT() => (T)(object)new C1 { F1 = 123 }; static void Test1() { _ = GetT()[Get1(), $"", Get1()]; } - static void Test2() where T : struct + static void Test2() where T : class { _ = GetT()[Get1(), $"", Get1()]; } @@ -16195,20 +17126,21 @@ .locals init (T V_0) "); } - [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_05() + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_ReadonlyReceiver_020(string refKind) { - var src = """ -using System.Threading.Tasks; - + var src = $$$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler +struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, ref TR x) + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) { - System.Console.Write(((S1)(object)x).F1); - x = (TR)(object)new S1 { F1 = ((S1)(object)x).F1 + 1 }; + System.Console.Write(x.F1); + Program.F.F1++; } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; @@ -16216,13 +17148,13 @@ public void AppendLiteral(string value) { } static class E { - extension(ref T x) where T : struct + extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(((S1)(object)x).F1); + System.Console.Write(x.F1); return 0; } } @@ -16236,79 +17168,160 @@ struct S1 class Program { - static async Task Main() + public static S1 F; + + static void Main() { - Test2(); + F = new S1 { F1 = 123 }; + Test({{{(refKind == "ref" ? "ref" : "in")}}} F); + System.Console.Write(F.F1); + } - System.Console.Write(":"); + static void Test({{{refKind}}} S1 x) + { + _ = x[Program.Get1(), $"", Get1()]; + } - await Test3(); + public static int Get1() + { + Program.F.F1++; + return 1; } +} +"""; - static void Test2() where T : struct + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 29 (0x1d) + .maxstack 5 + .locals init (S1& V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""int Program.Get1()"" + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.0 + IL_000b: ldloc.0 + IL_000c: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0011: call ""int Program.Get1()"" + IL_0016: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001b: pop + IL_001c: ret +} +"); + } + + [Theory] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_ReadonlyReceiver_021(string refKind) { - _ = GetT()[Get1(), $"", Get1()]; + var src = $$$""" +[System.Runtime.CompilerServices.InterpolatedStringHandler] +struct InterpolationHandler +{ + + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) + { + System.Console.Write(x.F1); + Program.F.F1++; } + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} - static T GetT() => (T)(object)new S1 { F1 = 123 }; +static class E +{ + extension({{{refKind}}} S1 x) + { + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + { + get + { + System.Console.Write(x.F1); + return 0; + } + } + } +} - static int Get1() +struct S1 +{ + public int F1; +} + +class Program +{ + public static S1 F; + + static void Main() { - return 1; + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); } - static async Task Test3() where T : struct + static void Test() { - _ = GetT()[Get1(), $"", await Get1Async()]; + _ = GetS1()[Program.Get1(), $"", Get1()]; } - static async Task Get1Async() + static {{{(refKind == "ref" ? "ref" : "ref readonly")}}} S1 GetS1() => ref Program.F; + + public static int Get1() { - await Task.Yield(); + Program.F.F1++; return 1; } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126").VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2()", + verifier.VerifyIL("Program.Test", @" { - // Code size 35 (0x23) + // Code size 33 (0x21) .maxstack 5 - .locals init (T V_0) + .locals init (S1& V_0) IL_0000: nop - IL_0001: call ""T Program.GetT()"" + IL_0001: call """ + (refKind == "ref" ? "ref" : "ref readonly") + @" S1 Program.GetS1()"" IL_0006: stloc.0 - IL_0007: ldloca.s V_0 - IL_0009: call ""int Program.Get1()"" + IL_0007: ldloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 - IL_000f: ldc.i4.0 - IL_0010: ldloca.s V_0 - IL_0012: newobj ""InterpolationHandler..ctor(int, int, ref T)"" - IL_0017: call ""int Program.Get1()"" - IL_001c: call ""int E.get_Item(ref T, int, InterpolationHandler, int)"" - IL_0021: pop - IL_0022: ret + IL_000f: ldloc.0 + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" + IL_0015: call ""int Program.Get1()"" + IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" + IL_001f: pop + IL_0020: ret } "); } - [Fact] - public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_06() + [Theory] + [InlineData("ref readonly")] + [InlineData("in")] + public void IndexerAccess_Get_WithInterpolationHandler_ReadonlyReceiver_022(string refKind) { - var src = """ -using System.Threading.Tasks; - + var src = $$$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] -struct InterpolationHandler +struct InterpolationHandler { - public InterpolationHandler(int literalLength, int formattedCount, TR x) + public InterpolationHandler(int literalLength, int formattedCount, {{{refKind}}} S1 x) { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(x.F1); + Program.Increment(); } public void AppendLiteral(string value) { } public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; @@ -16316,111 +17329,84 @@ public void AppendLiteral(string value) { } static class E { - extension(T x) + extension({{{refKind}}} S1 x) { - public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] + public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("x")] InterpolationHandler h, int j] { get { - System.Console.Write(((C1)(object)x).F1); + System.Console.Write(x.F1); return 0; } } } } -class C1 +struct S1 { public int F1; } class Program { - static async Task Main() - { - Test1(); - - System.Console.Write(":"); - - Test2(); - - System.Console.Write(":"); - - await Test3(); - } - - static T GetT() => (T)(object)new C1 { F1 = 123 }; + public static readonly S1 F; - static void Test1() + static void Main() { - _ = GetT()[Get1(), $"", Get1()]; + Initialize(); + Test(); + System.Console.Write(F.F1); } - static void Test2() where T : class + static unsafe void Initialize() { - _ = GetT()[Get1(), $"", Get1()]; + fixed (int* f1 = &F.F1) + { + *f1 = 123; + } } - static int Get1() + public static unsafe void Increment() { - return 1; + fixed (int* f1 = &F.F1) + { + (*f1)++; + } } - static async Task Test3() + static void Test() { - _ = GetT()[Get1(), $"", await Get1Async()]; + _ = F[Program.Get1(), $"", Get1()]; } - static async Task Get1Async() + public static int Get1() { - await Task.Yield(); + Increment(); return 1; } } """; - var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123:123123").VerifyDiagnostics(); - - verifier.VerifyIL("Program.Test1()", -@" -{ - // Code size 33 (0x21) - .maxstack 5 - .locals init (T V_0) - IL_0000: nop - IL_0001: call ""T Program.GetT()"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: call ""int Program.Get1()"" - IL_000d: ldc.i4.0 - IL_000e: ldc.i4.0 - IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" - IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" - IL_001f: pop - IL_0020: ret -} -"); + var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + var verifier = CompileAndVerify(comp, expectedOutput: "124126126", verify: Verification.Skipped).VerifyDiagnostics(); - verifier.VerifyIL("Program.Test2()", + verifier.VerifyIL("Program.Test", @" { // Code size 33 (0x21) .maxstack 5 - .locals init (T V_0) + .locals init (S1& V_0) IL_0000: nop - IL_0001: call ""T Program.GetT()"" + IL_0001: ldsflda ""S1 Program.F"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call ""int Program.Get1()"" IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 IL_000f: ldloc.0 - IL_0010: newobj ""InterpolationHandler..ctor(int, int, T)"" + IL_0010: newobj ""InterpolationHandler..ctor(int, int, " + refKind + @" S1)"" IL_0015: call ""int Program.Get1()"" - IL_001a: call ""int E.get_Item(T, int, InterpolationHandler, int)"" + IL_001a: call ""int E.get_Item(" + refKind + @" S1, int, InterpolationHandler, int)"" IL_001f: pop IL_0020: ret } @@ -16490,36 +17476,49 @@ public static int Get1() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125:123125", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 23 (0x17) + // Code size 32 (0x20) .maxstack 3 + .locals init (int V_0, + int V_1) IL_0000: nop - IL_0001: ldsfld ""S1 Program.F"" + IL_0001: ldsflda ""S1 Program.F"" IL_0006: call ""int Program.Get1()"" - IL_000b: call ""int Program.Get1()"" - IL_0010: call ""void E.set_Item(S1, int, int)"" - IL_0015: nop - IL_0016: ret + IL_000b: stloc.0 + IL_000c: call ""int Program.Get1()"" + IL_0011: stloc.1 + IL_0012: ldobj ""S1"" + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: call ""void E.set_Item(S1, int, int)"" + IL_001e: nop + IL_001f: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 24 (0x18) + // Code size 28 (0x1c) .maxstack 3 + .locals init (int V_0, + int V_1) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int Program.Get1()"" - IL_0011: call ""void E.set_Item(S1, int, int)"" - IL_0016: nop - IL_0017: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: stloc.1 + IL_000e: ldobj ""S1"" + IL_0013: ldloc.0 + IL_0014: ldloc.1 + IL_0015: call ""void E.set_Item(S1, int, int)"" + IL_001a: nop + IL_001b: ret } "); @@ -16844,37 +17843,64 @@ static int Get1() """; var comp = CreateCompilation([src], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123125:123125").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "125125:125125").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 24 (0x18) + // Code size 58 (0x3a) .maxstack 3 + .locals init (T V_0, + T& V_1, + int V_2, + int V_3, + T V_4) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int Program.Get1()"" - IL_0011: call ""void E.set_Item(T, int, int)"" - IL_0016: nop - IL_0017: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_4 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_4 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.1 + IL_0015: ldobj ""T"" + IL_001a: stloc.0 + IL_001b: ldloca.s V_0 + IL_001d: br.s IL_0020 + IL_001f: ldloc.1 + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: call ""int Program.Get1()"" + IL_002b: stloc.3 + IL_002c: ldobj ""T"" + IL_0031: ldloc.2 + IL_0032: ldloc.3 + IL_0033: call ""void E.set_Item(T, int, int)"" + IL_0038: nop + IL_0039: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 24 (0x18) + // Code size 28 (0x1c) .maxstack 3 + .locals init (int V_0, + int V_1) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int Program.Get1()"" - IL_0011: call ""void E.set_Item(T, int, int)"" - IL_0016: nop - IL_0017: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: call ""int Program.Get1()"" + IL_000d: stloc.1 + IL_000e: ldobj ""T"" + IL_0013: ldloc.0 + IL_0014: ldloc.1 + IL_0015: call ""void E.set_Item(T, int, int)"" + IL_001a: nop + IL_001b: ret } "); @@ -17174,16 +18200,37 @@ static int Get1() verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 24 (0x18) + // Code size 58 (0x3a) .maxstack 3 + .locals init (T V_0, + T& V_1, + int V_2, + int V_3, + T V_4) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int Program.Get1()"" - IL_0011: call ""void E.set_Item(T, int, int)"" - IL_0016: nop - IL_0017: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_4 + IL_0005: initobj ""T"" + IL_000b: ldloc.s V_4 + IL_000d: box ""T"" + IL_0012: brtrue.s IL_001f + IL_0014: ldloc.1 + IL_0015: ldobj ""T"" + IL_001a: stloc.0 + IL_001b: ldloca.s V_0 + IL_001d: br.s IL_0020 + IL_001f: ldloc.1 + IL_0020: call ""int Program.Get1()"" + IL_0025: stloc.2 + IL_0026: call ""int Program.Get1()"" + IL_002b: stloc.3 + IL_002c: ldobj ""T"" + IL_0031: ldloc.2 + IL_0032: ldloc.3 + IL_0033: call ""void E.set_Item(T, int, int)"" + IL_0038: nop + IL_0039: ret } "); @@ -17458,34 +18505,41 @@ public static int Get1() """; var comp = CreateCompilation([src], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" { - // Code size 18 (0x12) + // Code size 25 (0x19) .maxstack 2 + .locals init (int V_0) IL_0000: nop - IL_0001: ldsfld ""S1 Program.F"" + IL_0001: ldsflda ""S1 Program.F"" IL_0006: call ""int Program.Get1()"" - IL_000b: call ""int E.get_Item(S1, int)"" - IL_0010: pop - IL_0011: ret + IL_000b: stloc.0 + IL_000c: ldobj ""S1"" + IL_0011: ldloc.0 + IL_0012: call ""int E.get_Item(S1, int)"" + IL_0017: pop + IL_0018: ret } "); verifier.VerifyIL("S1.Test", @" { - // Code size 19 (0x13) + // Code size 21 (0x15) .maxstack 2 + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""S1"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int E.get_Item(S1, int)"" - IL_0011: pop - IL_0012: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""int E.get_Item(S1, int)"" + IL_0013: pop + IL_0014: ret } "); } @@ -17763,35 +18817,56 @@ static int Get1() """; var comp = CreateCompilation([src], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123124:123124").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 19 (0x13) + // Code size 50 (0x32) .maxstack 2 + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int E.get_Item(T, int)"" - IL_0011: pop - IL_0012: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""int E.get_Item(T, int)"" + IL_0030: pop + IL_0031: ret } "); verifier.VerifyIL("Program.Test2(ref T)", @" { - // Code size 19 (0x13) + // Code size 21 (0x15) .maxstack 2 + .locals init (int V_0) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int E.get_Item(T, int)"" - IL_0011: pop - IL_0012: ret + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""T"" + IL_000d: ldloc.0 + IL_000e: call ""int E.get_Item(T, int)"" + IL_0013: pop + IL_0014: ret } "); } @@ -18028,15 +19103,33 @@ static int Get1() verifier.VerifyIL("Program.Test1(ref T)", @" { - // Code size 19 (0x13) + // Code size 50 (0x32) .maxstack 2 + .locals init (T V_0, + T& V_1, + int V_2, + T V_3) IL_0000: nop IL_0001: ldarg.0 - IL_0002: ldobj ""T"" - IL_0007: call ""int Program.Get1()"" - IL_000c: call ""int E.get_Item(T, int)"" - IL_0011: pop - IL_0012: ret + IL_0002: stloc.1 + IL_0003: ldloca.s V_3 + IL_0005: initobj ""T"" + IL_000b: ldloc.3 + IL_000c: box ""T"" + IL_0011: brtrue.s IL_001e + IL_0013: ldloc.1 + IL_0014: ldobj ""T"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_0 + IL_001c: br.s IL_001f + IL_001e: ldloc.1 + IL_001f: call ""int Program.Get1()"" + IL_0024: stloc.2 + IL_0025: ldobj ""T"" + IL_002a: ldloc.2 + IL_002b: call ""int E.get_Item(T, int)"" + IL_0030: pop + IL_0031: ret } "); From eacecf15204184bf8c3762822d255a1d7f43169c Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 18 Jul 2025 11:51:45 -0700 Subject: [PATCH 6/9] Revert "Temporarily add limited support for extension indexers" This reverts commit c2412e0b5ecd6b14f4ba9d7a66a52e05f511128f. --- .../Portable/Binder/Binder_Expressions.cs | 49 ------------- .../Portable/Binder/RefSafetyAnalysis.cs | 2 +- .../Portable/FlowAnalysis/NullableWalker.cs | 71 ++++--------------- .../Source/SourceMemberContainerSymbol.cs | 4 +- 4 files changed, 17 insertions(+), 109 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 408a67f1d7cc2..a2c295df9befa 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -10002,35 +10002,6 @@ private BoundExpression BindIndexerAccess(SyntaxNode node, BoundExpression expr, } else { - foreach (var scope in new ExtensionScopes(this)) - { - lookupResult.Clear(); - - scope.Binder.LookupAllExtensionMembersInSingleBinder( - lookupResult, WellKnownMemberNames.Indexer, arity: 0, lookupOptions, - originalBinder: this, useSiteInfo: ref useSiteInfo, classicExtensionUseSiteInfo: ref useSiteInfo); - - if (lookupResult.IsMultiViable) - { - ArrayBuilder indexerGroup = ArrayBuilder.GetInstance(); - foreach (Symbol symbol in lookupResult.Symbols) - { - Debug.Assert(symbol.IsIndexer()); - indexerGroup.Add((PropertySymbol)symbol); - } - - var actualMethodArguments = AnalyzedArguments.GetInstance(); - CombineExtensionMethodArguments(expr, analyzedArguments, actualMethodArguments); - - indexerAccessExpression = BindIndexerOrIndexedPropertyAccess(node, expr, indexerGroup, actualMethodArguments, diagnostics); - indexerGroup.Free(); - - actualMethodArguments.Free(); - lookupResult.Free(); - return indexerAccessExpression; - } - } - indexerAccessExpression = BadIndexerExpression(node, expr, analyzedArguments, lookupResult.Error, diagnostics); } } @@ -10271,26 +10242,6 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( // Make sure that the result of overload resolution is valid. var gotError = MemberGroupFinalValidationAccessibilityChecks(receiver, property, syntax, diagnostics, invokedAsExtensionMethod: false); - if (property.GetIsNewExtensionMember()) - { - // For new extension methods, we performed overload resolution giving the receiver as one of the arguments. - // We now restore the arguments to their original state and update the result accordingly. - analyzedArguments.Arguments.RemoveAt(0); - - if (analyzedArguments.Names is { Count: > 0 }) - { - analyzedArguments.Names.RemoveAt(0); - } - - if (analyzedArguments.RefKinds is { Count: > 0 }) - { - analyzedArguments.RefKinds.RemoveAt(0); - } - - Debug.Assert(resolutionResult.Result.ConversionForArg(0).Exists); - resolutionResult = resolutionResult.WithResult(resolutionResult.Result.WithoutReceiverArgument()); - } - receiver = ReplaceTypeOrValueReceiver(receiver, property.IsStatic, diagnostics); ImmutableArray argsToParams; diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index 85ce032683691..00f9037787a3e 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -283,7 +283,7 @@ private void TrackVisit(BoundNode? node) if (_visited is { } && _visited.Count <= MaxTrackVisited) { bool added = _visited.Add(expr); - //RoslynDebug.Assert(added, $"Expression {expr} `{expr.Syntax}` visited more than once."); + RoslynDebug.Assert(added, $"Expression {expr} `{expr.Syntax}` visited more than once."); } } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 32595a3f0d46f..2a470a46ea216 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -11378,68 +11378,25 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitIndexerAccess(BoundIndexerAccess node) { - var indexer = node.Indexer; + var receiverOpt = node.ReceiverOpt; + var receiverType = VisitRvalueWithState(receiverOpt).Type; + // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null + // after indices have been visited, and only if the receiver has not changed. + // Tracked by https://github.com/dotnet/roslyn/issues/78829: add support for indexers + _ = CheckPossibleNullReceiver(receiverOpt); - if (indexer.GetIsNewExtensionMember()) + var indexer = node.Indexer; + if (receiverType is object) { - Debug.Assert(node.ReceiverOpt is not null); - ImmutableArray arguments = [node.ReceiverOpt, ..node.Arguments]; - - var extensionParameter = indexer.ContainingType.ExtensionParameter; - Debug.Assert(extensionParameter is not null); - ImmutableArray parameters = [extensionParameter, ..indexer.Parameters]; - RefKind receiverRefKind = extensionParameter.RefKind == RefKind.Ref ? RefKind.Ref : RefKind.None; - var argumentRefKindsOpt = node.ArgumentRefKindsOpt; - - if (argumentRefKindsOpt.IsDefault) - { - if (receiverRefKind != RefKind.None) - { - var builder = ArrayBuilder.GetInstance(node.Arguments.Length + 1, fillWithValue: RefKind.None); - builder[0] = receiverRefKind; - argumentRefKindsOpt = builder.ToImmutableAndFree(); - } - } - else - { - argumentRefKindsOpt = [receiverRefKind, .. argumentRefKindsOpt]; - } - - // Tracked by https://github.com/dotnet/roslyn/issues/37238 : properties/indexers should account for NotNullIfNotNull - bool returnNotNull; - (var updatedProperty, _, returnNotNull) = VisitArguments(node, arguments, argumentRefKindsOpt, parameters, default, defaultArguments: default, - expanded: false, invokedAsExtensionMethod: false, indexer, firstArgumentResult: null); - - Debug.Assert(updatedProperty is not null); - - TypeWithAnnotations typeWithAnnotations = GetTypeOrReturnTypeWithAnnotations(updatedProperty); - FlowAnalysisAnnotations memberAnnotations = GetRValueAnnotations(updatedProperty); - TypeWithState typeWithState = ApplyUnconditionalAnnotations(typeWithAnnotations.ToTypeWithState(), memberAnnotations); - - SetResult(node, typeWithState, typeWithAnnotations); - SetUpdatedSymbol(node, node.Indexer, updatedProperty); + // Update indexer based on inferred receiver type. + indexer = (PropertySymbol)AsMemberOfType(receiverType, indexer); } - else - { - var receiverOpt = node.ReceiverOpt; - var receiverType = VisitRvalueWithState(receiverOpt).Type; - // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null - // after indices have been visited, and only if the receiver has not changed. - // Tracked by https://github.com/dotnet/roslyn/issues/78829: add support for indexers - _ = CheckPossibleNullReceiver(receiverOpt); - if (receiverType is object) - { - // Update indexer based on inferred receiver type. - indexer = (PropertySymbol)AsMemberOfType(receiverType, indexer); - } + VisitArguments(node, node.Arguments, node.ArgumentRefKindsOpt, indexer, node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded); - VisitArguments(node, node.Arguments, node.ArgumentRefKindsOpt, indexer, node.ArgsToParamsOpt, node.DefaultArguments, node.Expanded); - - var resultType = ApplyUnconditionalAnnotations(indexer.TypeWithAnnotations.ToTypeWithState(), GetRValueAnnotations(indexer)); - SetResult(node, resultType, indexer.TypeWithAnnotations); - SetUpdatedSymbol(node, node.Indexer, indexer); - } + var resultType = ApplyUnconditionalAnnotations(indexer.TypeWithAnnotations.ToTypeWithState(), GetRValueAnnotations(indexer)); + SetResult(node, resultType, indexer.TypeWithAnnotations); + SetUpdatedSymbol(node, node.Indexer, indexer); return null; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index b3a2b24bf2fa7..fb9b111e317e1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -4682,11 +4682,11 @@ internal static bool IsAllowedExtensionMember(Symbol member) break; case SymbolKind.Property: - //if (!((PropertySymbol)member).IsIndexer) + if (!((PropertySymbol)member).IsIndexer) { return true; } - //break; + break; case SymbolKind.Field: case SymbolKind.Event: From 814baf66da30869e33a17022a8bfad507df07933 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 18 Jul 2025 12:40:29 -0700 Subject: [PATCH 7/9] Disable tests for indexers --- .../LocalRewriter_IndexerAccess.cs | 1 + .../Test/Emit3/Semantics/ExtensionTests2.cs | 177 ++++++++++++------ 2 files changed, 119 insertions(+), 59 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index d984f84e88159..dce87140f8840 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -612,6 +612,7 @@ private BoundExpression GetUnderlyingIndexerOrSliceAccess( // callers do the caching instead // Tracked by https://github.com/dotnet/roslyn/issues/71056 AddPlaceholderReplacement(argumentPlaceholder, integerArgument); + // https://github.com/dotnet/roslyn/issues/78829 - Do we need to do something special for recievers of extension indexers here? ImmutableArray rewrittenArguments = VisitArgumentsAndCaptureReceiverIfNeeded( ref receiver, forceReceiverCapturing: false, diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index cee116de88a37..f74604ea72ee3 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -11758,7 +11758,8 @@ .locals init (int V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_01() { var src = """ @@ -11913,7 +11914,8 @@ static void Test() ); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -12064,7 +12066,8 @@ static void Test() ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_03() { var src = """ @@ -12152,7 +12155,8 @@ .locals init (C1 V_0, "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_04() { var src = """ @@ -12357,7 +12361,8 @@ static class E ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_05() { var src = """ @@ -12510,7 +12515,8 @@ static class E ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_06() { var src = """ @@ -12663,7 +12669,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_07() { var src = """ @@ -12755,7 +12762,8 @@ .locals init (int V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_08() { var src = """ @@ -12878,7 +12886,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_01() { var src = """ @@ -13075,7 +13084,8 @@ public void AppendLiteral(string value) { } ); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -13263,7 +13273,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_03() { var src = """ @@ -13366,7 +13377,8 @@ .locals init (C1 V_0, "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_04() { var src = """ @@ -13626,7 +13638,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_05() { var src = """ @@ -13814,7 +13827,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_06() { var src = """ @@ -14006,7 +14020,8 @@ .locals init (T V_0, "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_07() { @@ -14130,7 +14145,8 @@ .locals init (T V_0, "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79415")] public void IndexerAccess_CompoundAssignment_WithInterpolationHandler_08() { @@ -14295,7 +14311,8 @@ .locals init (T V_0, "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_WithInterpolationHandler_01() { var src = """ @@ -14478,7 +14495,8 @@ public void AppendLiteral(string value) { } ); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -14644,7 +14662,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_WithInterpolationHandler_03() { var src = """ @@ -14736,7 +14755,8 @@ .locals init (C1 V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Set_WithInterpolationHandler_04() { @@ -14989,7 +15009,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_WithInterpolationHandler_05() { var src = """ @@ -15166,7 +15187,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Set_WithInterpolationHandler_06() { @@ -15346,7 +15368,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_WithInterpolationHandler_07() { var src = """ @@ -15451,7 +15474,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_WithInterpolationHandler_08() { var src = """ @@ -15586,7 +15610,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_01() { var src = """ @@ -15728,7 +15753,8 @@ .locals init (S1& V_0, "); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -15887,7 +15913,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_03() { var src = """ @@ -15974,7 +16001,8 @@ .locals init (C1 V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_04() { @@ -16160,7 +16188,8 @@ .locals init (T& V_0, "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_05() { var src = """ @@ -16331,7 +16360,8 @@ public void AppendLiteral(string value) { } ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Get_WithInterpolationHandler_LValueReceiver_06() { @@ -16506,7 +16536,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_01() { var src = """ @@ -16589,7 +16620,8 @@ .locals init (S1 V_0) "); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -16679,7 +16711,8 @@ .locals init (S1 V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_03() { var src = """ @@ -16762,7 +16795,8 @@ .locals init (C1 V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_04() { var src = """ @@ -16894,7 +16928,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_05() { var src = """ @@ -16995,7 +17030,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_WithInterpolationHandler_RValueReceiver_06() { var src = """ @@ -17126,7 +17162,8 @@ .locals init (T V_0) "); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -17216,7 +17253,8 @@ .locals init (S1& V_0) "); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -17308,7 +17346,8 @@ .locals init (S1& V_0) "); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref readonly")] [InlineData("in")] public void IndexerAccess_Get_WithInterpolationHandler_ReadonlyReceiver_022(string refKind) @@ -17413,7 +17452,8 @@ .locals init (S1& V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_01() { var src = """ @@ -17550,7 +17590,8 @@ static void Test() ); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -17678,7 +17719,8 @@ static void Test() ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_03() { var src = """ @@ -17750,7 +17792,8 @@ .maxstack 3 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Set_04() { @@ -17956,7 +17999,8 @@ static class E ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_05() { var src = """ @@ -18102,7 +18146,8 @@ static class E ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Set_06() { @@ -18251,7 +18296,8 @@ .maxstack 3 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_07() { var src = """ @@ -18336,7 +18382,8 @@ .maxstack 3 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Set_08() { var src = """ @@ -18445,7 +18492,8 @@ .maxstack 3 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_LValueReceiver_01() { var src = """ @@ -18544,7 +18592,8 @@ .locals init (int V_0) "); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -18663,7 +18712,8 @@ static void Test() ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_LValueReceiver_03() { var src = """ @@ -18729,7 +18779,8 @@ .maxstack 2 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Get_LValueReceiver_04() { @@ -18871,7 +18922,8 @@ .locals init (int V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_LValueReceiver_05() { var src = """ @@ -19010,7 +19062,8 @@ static class E ); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [WorkItem("https://github.com/dotnet/roslyn/issues/79416")] public void IndexerAccess_Get_LValueReceiver_06() { @@ -19149,7 +19202,8 @@ .maxstack 2 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_RValueReceiver_01() { var src = """ @@ -19212,7 +19266,8 @@ .maxstack 2 "); } - [Theory] + [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] @@ -19281,7 +19336,8 @@ .locals init (S1 V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_RValueReceiver_03() { var src = """ @@ -19344,7 +19400,8 @@ .maxstack 2 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_RValueReceiver_04() { var src = """ @@ -19447,7 +19504,8 @@ .maxstack 2 "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_RValueReceiver_05() { var src = """ @@ -19530,7 +19588,8 @@ .locals init (T V_0) "); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] + [WorkItem("https://github.com/dotnet/roslyn/issues/78829")] public void IndexerAccess_Get_RValueReceiver_06() { var src = """ From 3951497f22d4692a6c94775edf044d617cb53948 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 25 Jul 2025 10:35:27 -0700 Subject: [PATCH 8/9] PR feedback --- .../LocalRewriter_AssignmentOperator.cs | 14 ++-- .../LocalRewriter/LocalRewriter_Call.cs | 8 +- ...ocalRewriter_CompoundAssignmentOperator.cs | 2 +- .../LocalRewriter_IndexerAccess.cs | 10 +-- .../Test/Emit3/Semantics/ExtensionTests.cs | 8 ++ .../Test/Emit3/Semantics/ExtensionTests2.cs | 36 +++++++- .../Semantic/Semantics/InterpolationTests.cs | 84 +++++++++++++++++++ 7 files changed, 144 insertions(+), 18 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index 983b0e7166a3c..0a39857de9e50 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -277,7 +277,7 @@ private BoundExpression MakeStaticAssignmentOperator( } } - private bool IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(BoundExpression rewrittenReceiver, PropertySymbol property) + private bool IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReads(BoundExpression rewrittenReceiver, PropertySymbol property) { return CanChangeValueBetweenReads(rewrittenReceiver, localsMayBeAssignedOrCaptured: true, structThisCanChangeValueBetweenReads: true) && IsNewExtensionMemberWithByValPossiblyStructReceiver(property) && @@ -324,14 +324,14 @@ private BoundExpression MakePropertyAssignment( if (rewrittenReceiver is not null && assignmentKind is not (AssignmentKind.CompoundAssignment or AssignmentKind.NullCoalescingAssignment or AssignmentKind.Deconstruction or AssignmentKind.IncrementDecrement) && - IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, property) && + IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReads(rewrittenReceiver, property) && (arguments.Length != 0 || !IsSafeForReordering(rewrittenRight, RefKind.None))) { - // The receiever has location, but extension property/indexer takes receiver by value. - // This means that we need to ensure that the the receiver value is read after + // The receiver has location, but extension property/indexer takes receiver by value. + // This means that we need to ensure that the receiver value is read after // any side-effecting arguments and right hand side are evaluated, so that the - // the setter receives the last value of the receiver, not the value before the - // arguments/rhs were evaluated. Receiver sideeffects should be evaluated at + // setter receives the last value of the receiver, not the value before the + // arguments/rhs were evaluated. Receiver side effects should be evaluated at // the very beginning, of course. needSpecialExtensionPropertyReceiverReadOrder = true; @@ -415,7 +415,7 @@ assignmentKind is not (AssignmentKind.CompoundAssignment or AssignmentKind.NullC return new BoundSequence( syntax, AppendToPossibleNull(argTemps, rhsTemp), - sideEffects.Add(setterCall), // https://github.com/dotnet/roslyn/issues/78829 - there is no test coverage for sideEffects on this code path + sideEffects.Add(setterCall), // https://github.com/dotnet/roslyn/issues/78829 - there is no test coverage for 'sideEffects' on this code path boundRhs, rhsTemp.Type); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 3421da09193fe..c0d305fd7cc53 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -649,13 +649,13 @@ internal static bool IsCapturedPrimaryConstructorParameter(BoundExpression expre /// interpolated string handler conversions needs), must be not null. /// /// If receiver is captured by this method: - /// - If is not null, the sideeffect of capturing is added to + /// - If is not null, the side effect of capturing is added to /// and is changed to the captured value; /// - Otherwise, is changed to a node with no locals, - /// the sideeffects of capturing are the sifeeffects of the sequence and its result is the captured value. + /// the side effects of capturing are the side effects of the sequence and its result is the captured value. /// /// All temps introduced by this function for capturing purposes (including the temp capturing the receiver) are appended - /// to , which is allocated, if 'null' on input. + /// to , which is allocated if 'null' on input. /// private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded( [NotNullIfNotNull(nameof(rewrittenReceiver))] ref BoundExpression? rewrittenReceiver, @@ -727,7 +727,7 @@ private ImmutableArray VisitArgumentsAndCaptureReceiverIfNeeded } } - receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, (refKind is RefKind.RefReadOnlyParameter or RefKind.In) ? RefKindExtensions.StrictIn : refKind); + receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind is RefKind.RefReadOnlyParameter ? RefKind.In : refKind); tempsOpt ??= ArrayBuilder.GetInstance(); tempsOpt.Add(receiverTemp.LocalSymbol); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index e309736925d02..91f162a64b4ca 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -323,7 +323,7 @@ static bool IsNewExtensionMemberWithByValPossiblyStructReceiver(Symbol symbol) var receiverTemp = _factory.StoreToTemp( rewrittenReceiver, out assignmentToTemp, - refKind: refKind, + refKind: refKind is RefKind.RefReadOnlyParameter ? RefKind.In : refKind, isKnownToReferToTempIfReferenceType: isKnownToReferToTempIfReferenceType); temps.Add(receiverTemp.LocalSymbol); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index dce87140f8840..e817e47236067 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -162,13 +162,13 @@ private BoundExpression MakeIndexerAccess( bool needSpecialExtensionPropertyReceiverReadOrder = false; ArrayBuilder? storesOpt = null; - if (IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, indexer)) + if (IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReads(rewrittenReceiver, indexer)) { - // The receiever has location, but extension indexer takes receiver by value. - // This means that we need to ensure that the the receiver value is read after + // The receiver has location, but extension indexer takes receiver by value. + // This means that we need to ensure that the receiver value is read after // any side-effecting arguments are evaluated, so that the - // the setter receives the last value of the receiver, not the value before the - // arguments were evaluated. Receiver sideeffects should be evaluated at + // setter receives the last value of the receiver, not the value before the + // arguments were evaluated. Receiver side effects should be evaluated at // the very beginning, of course. needSpecialExtensionPropertyReceiverReadOrder = true; diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index d375b7ef5eec8..ba9e2333b2299 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -21049,6 +21049,7 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRef(bool useMetadataRef) { @@ -21193,6 +21194,7 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_ReceiverParameter_Generic_ByRef(bool useMetadataRef) { @@ -21292,6 +21294,7 @@ .locals init (T& V_0) } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByIn_WithConstantReceiver(bool useMetadataRef, [CombinatorialValues("ref readonly", "in")] string refkind) { @@ -21686,6 +21689,7 @@ .locals init (MyStruct V_0) } [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_StructReceiverParameter_ByValueThroughField(bool useMetadataRef) { @@ -21768,6 +21772,7 @@ .locals init (MyStruct V_0) } [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_StructReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) { @@ -21880,6 +21885,7 @@ .locals init (T V_0) } [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_StructReceiverParameter_GenericStruct_ByValueThroughField(bool useMetadataRef) { @@ -21976,6 +21982,7 @@ .locals init (T V_0) } [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_ClassReceiverParameter_GenericClass_ByValueThroughField(bool useMetadataRef) { @@ -22089,6 +22096,7 @@ .locals init (T V_0) } [Theory] + [WorkItem("https://github.com/dotnet/roslyn/issues/79379")] [CombinatorialData] public void InterpolationHandler_ClassReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) { diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index f74604ea72ee3..017ce7ae4b785 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -5596,6 +5596,28 @@ public void Test() { this.P1 = Program.Get1(); } + + public int P2 + { + get + { + return 0; + } + set + { + System.Console.Write(F1); + } + } + + public void Test2() + { + this.P2 = Program.Get1(); + } + + public void Test3() + { + E.set_P1(this, Program.Get1()); + } } class Program @@ -5613,6 +5635,18 @@ static void Main() F = new S1 { F1 = 123 }; F.Test(); System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test2(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test3(); + System.Console.Write(F.F1); } static void Test() @@ -5629,7 +5663,7 @@ public static int Get1() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124:124124:123124", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", @" diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 7ae0f73ffafb3..43f93eb401350 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -11687,6 +11687,90 @@ Creating DummyHandler verifier.VerifyDiagnostics(); } + [Fact] + public void StructReceiver_Lvalue_08() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +using System.Text; + +var l = new StructLogger(); +Console.WriteLine(""logged = {0}"", l._logged); +test(ref l); +Console.WriteLine(""logged = {0}"", l._logged); + +void test(ref readonly StructLogger l) +{ + l.Log($""log:{0}""); +} + +internal struct StructLogger +{ + public int _logged; + + public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) + { + _logged++; + Console.WriteLine($""StructLogger: "" + handler.GetContent()); + } +} + +[InterpolatedStringHandler] +internal ref struct DummyHandler +{ + private readonly StringBuilder _builder; + public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) + { + Console.WriteLine($""Creating DummyHandler""); + _builder = new StringBuilder(); + } + public string GetContent() => _builder.ToString(); + + public void AppendLiteral(string s) => _builder.Append(s); + public void AppendFormatted(T t) => _builder.Append(t); +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: @" +logged = 0 +Creating DummyHandler +StructLogger: log:0 +logged = 1 +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.<
$>g__test|0_0", +@" +{ + // Code size 45 (0x2d) + .maxstack 5 + .locals init (StructLogger& V_0, + DummyHandler V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldloca.s V_1 + IL_0005: ldc.i4.4 + IL_0006: ldc.i4.1 + IL_0007: ldloc.0 + IL_0008: ldobj ""StructLogger"" + IL_000d: call ""DummyHandler..ctor(int, int, StructLogger)"" + IL_0012: ldloca.s V_1 + IL_0014: ldstr ""log:"" + IL_0019: call ""void DummyHandler.AppendLiteral(string)"" + IL_001e: ldloca.s V_1 + IL_0020: ldc.i4.0 + IL_0021: call ""void DummyHandler.AppendFormatted(int)"" + IL_0026: ldloc.1 + IL_0027: call ""void StructLogger.Log(DummyHandler)"" + IL_002c: ret +} +"); + } + [Theory] [InlineData(@"$""""")] [InlineData(@"$"""" + $""""")] From f6cbd13ff31dd47676da7c79209a7e89788cce11 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Fri, 25 Jul 2025 11:07:35 -0700 Subject: [PATCH 9/9] Fixup a test baseline --- .../Semantic/Semantics/InterpolationTests.cs | 128 +++++++++++++++--- 1 file changed, 110 insertions(+), 18 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 43f93eb401350..a84140d4ecea2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -11737,7 +11737,7 @@ public DummyHandler(int literalLength, int formattedCount, StructLogger structLo logged = 0 Creating DummyHandler StructLogger: log:0 -logged = 1 +logged = 0 "); verifier.VerifyDiagnostics(); @@ -11745,28 +11745,120 @@ Creating DummyHandler verifier.VerifyIL("Program.<
$>g__test|0_0", @" { - // Code size 45 (0x2d) + // Code size 53 (0x35) .maxstack 5 .locals init (StructLogger& V_0, - DummyHandler V_1) + StructLogger V_1, + DummyHandler V_2) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 - IL_0003: ldloca.s V_1 - IL_0005: ldc.i4.4 - IL_0006: ldc.i4.1 - IL_0007: ldloc.0 - IL_0008: ldobj ""StructLogger"" - IL_000d: call ""DummyHandler..ctor(int, int, StructLogger)"" - IL_0012: ldloca.s V_1 - IL_0014: ldstr ""log:"" - IL_0019: call ""void DummyHandler.AppendLiteral(string)"" - IL_001e: ldloca.s V_1 - IL_0020: ldc.i4.0 - IL_0021: call ""void DummyHandler.AppendFormatted(int)"" - IL_0026: ldloc.1 - IL_0027: call ""void StructLogger.Log(DummyHandler)"" - IL_002c: ret + IL_0003: ldobj ""StructLogger"" + IL_0008: stloc.1 + IL_0009: ldloca.s V_1 + IL_000b: ldloca.s V_2 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.1 + IL_000f: ldloc.0 + IL_0010: ldobj ""StructLogger"" + IL_0015: call ""DummyHandler..ctor(int, int, StructLogger)"" + IL_001a: ldloca.s V_2 + IL_001c: ldstr ""log:"" + IL_0021: call ""void DummyHandler.AppendLiteral(string)"" + IL_0026: ldloca.s V_2 + IL_0028: ldc.i4.0 + IL_0029: call ""void DummyHandler.AppendFormatted(int)"" + IL_002e: ldloc.2 + IL_002f: call ""void StructLogger.Log(DummyHandler)"" + IL_0034: ret +} +"); + } + + [Fact] + public void StructReceiver_Lvalue_09() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +using System.Text; + +var l = new StructLogger(); +Console.WriteLine(""logged = {0}"", l._logged); +test(in l); +Console.WriteLine(""logged = {0}"", l._logged); + +void test(in StructLogger l) +{ + l.Log($""log:{0}""); +} + +internal struct StructLogger +{ + public int _logged; + + public void Log([InterpolatedStringHandlerArgument("""")] DummyHandler handler) + { + _logged++; + Console.WriteLine($""StructLogger: "" + handler.GetContent()); + } +} + +[InterpolatedStringHandler] +internal ref struct DummyHandler +{ + private readonly StringBuilder _builder; + public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger) + { + Console.WriteLine($""Creating DummyHandler""); + _builder = new StringBuilder(); + } + public string GetContent() => _builder.ToString(); + + public void AppendLiteral(string s) => _builder.Append(s); + public void AppendFormatted(T t) => _builder.Append(t); +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: @" +logged = 0 +Creating DummyHandler +StructLogger: log:0 +logged = 0 +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.<
$>g__test|0_0", +@" +{ + // Code size 53 (0x35) + .maxstack 5 + .locals init (StructLogger& V_0, + StructLogger V_1, + DummyHandler V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldobj ""StructLogger"" + IL_0008: stloc.1 + IL_0009: ldloca.s V_1 + IL_000b: ldloca.s V_2 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.1 + IL_000f: ldloc.0 + IL_0010: ldobj ""StructLogger"" + IL_0015: call ""DummyHandler..ctor(int, int, StructLogger)"" + IL_001a: ldloca.s V_2 + IL_001c: ldstr ""log:"" + IL_0021: call ""void DummyHandler.AppendLiteral(string)"" + IL_0026: ldloca.s V_2 + IL_0028: ldc.i4.0 + IL_0029: call ""void DummyHandler.AppendFormatted(int)"" + IL_002e: ldloc.2 + IL_002f: call ""void StructLogger.Log(DummyHandler)"" + IL_0034: ret } "); }