diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs index da9ca6e95398b..708f4aec15650 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs @@ -488,30 +488,47 @@ private BoundDecisionDag RewriteTupleInput( bool canShareInputs, out BoundExpression savedInputExpression) { - int count = loweredInput.Arguments.Length; + var tupleElements = loweredInput.Type.TupleElements; // first evaluate the inputs (in order) into temps var originalInput = BoundDagTemp.ForOriginalInput(loweredInput.Syntax, loweredInput.Type); - var newArguments = ArrayBuilder.GetInstance(loweredInput.Arguments.Length); - for (int i = 0; i < count; i++) + for (int i = 0; i < tupleElements.Length; i++) { - var field = loweredInput.Type.TupleElements[i].CorrespondingTupleField; + var field = tupleElements[i].CorrespondingTupleField; Debug.Assert(field != null); - var expr = loweredInput.Arguments[i]; + var expr = getExpression(i, loweredInput); var fieldFetchEvaluation = new BoundDagFieldEvaluation(expr.Syntax, field, originalInput); var temp = new BoundDagTemp(expr.Syntax, expr.Type, fieldFetchEvaluation); storeToTemp(temp, expr); - newArguments.Add(_tempAllocator.GetTemp(temp)); } var rewrittenDag = decisionDag.Rewrite(makeReplacement); savedInputExpression = loweredInput.Update( - loweredInput.Constructor, arguments: newArguments.ToImmutableAndFree(), loweredInput.ArgumentNamesOpt, loweredInput.ArgumentRefKindsOpt, + loweredInput.Constructor, arguments: loweredInput.Arguments, loweredInput.ArgumentNamesOpt, loweredInput.ArgumentRefKindsOpt, loweredInput.Expanded, loweredInput.ArgsToParamsOpt, loweredInput.ConstantValueOpt, loweredInput.InitializerExpressionOpt, loweredInput.BinderOpt, loweredInput.Type); return rewrittenDag; + static BoundExpression getExpression(int index, BoundObjectCreationExpression expression) + { + while (index >= NamedTypeSymbol.ValueTupleRestIndex) + { + if (expression.Arguments[NamedTypeSymbol.ValueTupleRestIndex] is BoundObjectCreationExpression restExpression) + { + expression = restExpression; + index -= NamedTypeSymbol.ValueTupleRestIndex; + } + else + { + // ValueTupleRestIndex should always be another tuple. Hence, it should always have BoundObjectCreationExpression. + throw ExceptionUtilities.Unreachable; + } + } + + return expression.Arguments[index]; + } + void storeToTemp(BoundDagTemp temp, BoundExpression expr) { if (canShareInputs && (expr.Kind == BoundKind.Parameter || expr.Kind == BoundKind.Local) && _tempAllocator.TrySetTemp(temp, expr)) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs index 4bc40f7cf3f89..43520748b574e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs @@ -2949,6 +2949,234 @@ public static void Main() Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(14, 17)); } + [Fact, WorkItem(47878, "https://github.com/dotnet/roslyn/issues/47878")] + public void Bug47878() + { + var text = @" +using System; +public class C +{ + public static void Main() + { + int x1 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) switch + { + (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => 1, + _ => -1, + }; + int x2 = (1, 2, 3, 4, 5, 6, 7, 8) switch + { + (1, 2, 3, 4, 5, 6, 7, 8) => 1, + _ => -1, + }; + int x3 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) switch + { + (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) => 1, + _ => -1, + }; + int x4 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) switch + { + (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) => 1, + _ => -1, + }; + Console.WriteLine($""{x1} {x2} {x3} {x4}""); + } +} +"; + var comp = CompileAndVerify(text, expectedOutput: "1 1 1 1"); + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(47878, "https://github.com/dotnet/roslyn/issues/47878")] + public void VerifyIL_8ElementsTuple() + { + var text = @" +using System; +public class C +{ + public static void Main() + { + int x = (1, 2, 3, 4, 5, 6, 7, 8) switch + { + (1, 2, 3, 4, 5, 6, 7, 8) => 1, + _ => -1, + }; + + Console.WriteLine(x); + } +} +"; + CompileAndVerify(text).VerifyIL("C.Main", @" +{ + // Code size 67 (0x43) + .maxstack 2 + .locals init (int V_0, + int V_1, + int V_2, + int V_3, + int V_4, + int V_5, + int V_6, + int V_7) + IL_0000: ldc.i4.1 + IL_0001: ldc.i4.2 + IL_0002: stloc.1 + IL_0003: ldc.i4.3 + IL_0004: stloc.2 + IL_0005: ldc.i4.4 + IL_0006: stloc.3 + IL_0007: ldc.i4.5 + IL_0008: stloc.s V_4 + IL_000a: ldc.i4.6 + IL_000b: stloc.s V_5 + IL_000d: ldc.i4.7 + IL_000e: stloc.s V_6 + IL_0010: ldc.i4.8 + IL_0011: stloc.s V_7 + IL_0013: ldc.i4.1 + IL_0014: bne.un.s IL_003a + IL_0016: ldloc.1 + IL_0017: ldc.i4.2 + IL_0018: bne.un.s IL_003a + IL_001a: ldloc.2 + IL_001b: ldc.i4.3 + IL_001c: bne.un.s IL_003a + IL_001e: ldloc.3 + IL_001f: ldc.i4.4 + IL_0020: bne.un.s IL_003a + IL_0022: ldloc.s V_4 + IL_0024: ldc.i4.5 + IL_0025: bne.un.s IL_003a + IL_0027: ldloc.s V_5 + IL_0029: ldc.i4.6 + IL_002a: bne.un.s IL_003a + IL_002c: ldloc.s V_6 + IL_002e: ldc.i4.7 + IL_002f: bne.un.s IL_003a + IL_0031: ldloc.s V_7 + IL_0033: ldc.i4.8 + IL_0034: bne.un.s IL_003a + IL_0036: ldc.i4.1 + IL_0037: stloc.0 + IL_0038: br.s IL_003c + IL_003a: ldc.i4.m1 + IL_003b: stloc.0 + IL_003c: ldloc.0 + IL_003d: call ""void System.Console.WriteLine(int)"" + IL_0042: ret +} +"); + } + + [Fact, WorkItem(47878, "https://github.com/dotnet/roslyn/issues/47878")] + public void VerifyIL_GreatherThan8ElementsTuple() + { + var text = @" +using System; +public class C +{ + public static void Main() + { + int x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) switch + { + (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) => 1, + _ => -1, + }; + + Console.WriteLine(x); + } +} +"; + CompileAndVerify(text).VerifyIL("C.Main", @" +{ + // Code size 117 (0x75) + .maxstack 2 + .locals init (int V_0, + int V_1, + int V_2, + int V_3, + int V_4, + int V_5, + int V_6, + int V_7, + int V_8, + int V_9, + int V_10, + int V_11, + int V_12) + IL_0000: ldc.i4.1 + IL_0001: ldc.i4.2 + IL_0002: stloc.1 + IL_0003: ldc.i4.3 + IL_0004: stloc.2 + IL_0005: ldc.i4.4 + IL_0006: stloc.3 + IL_0007: ldc.i4.5 + IL_0008: stloc.s V_4 + IL_000a: ldc.i4.6 + IL_000b: stloc.s V_5 + IL_000d: ldc.i4.7 + IL_000e: stloc.s V_6 + IL_0010: ldc.i4.8 + IL_0011: stloc.s V_7 + IL_0013: ldc.i4.s 9 + IL_0015: stloc.s V_8 + IL_0017: ldc.i4.s 10 + IL_0019: stloc.s V_9 + IL_001b: ldc.i4.s 11 + IL_001d: stloc.s V_10 + IL_001f: ldc.i4.s 12 + IL_0021: stloc.s V_11 + IL_0023: ldc.i4.s 13 + IL_0025: stloc.s V_12 + IL_0027: ldc.i4.1 + IL_0028: bne.un.s IL_006c + IL_002a: ldloc.1 + IL_002b: ldc.i4.2 + IL_002c: bne.un.s IL_006c + IL_002e: ldloc.2 + IL_002f: ldc.i4.3 + IL_0030: bne.un.s IL_006c + IL_0032: ldloc.3 + IL_0033: ldc.i4.4 + IL_0034: bne.un.s IL_006c + IL_0036: ldloc.s V_4 + IL_0038: ldc.i4.5 + IL_0039: bne.un.s IL_006c + IL_003b: ldloc.s V_5 + IL_003d: ldc.i4.6 + IL_003e: bne.un.s IL_006c + IL_0040: ldloc.s V_6 + IL_0042: ldc.i4.7 + IL_0043: bne.un.s IL_006c + IL_0045: ldloc.s V_7 + IL_0047: ldc.i4.8 + IL_0048: bne.un.s IL_006c + IL_004a: ldloc.s V_8 + IL_004c: ldc.i4.s 9 + IL_004e: bne.un.s IL_006c + IL_0050: ldloc.s V_9 + IL_0052: ldc.i4.s 10 + IL_0054: bne.un.s IL_006c + IL_0056: ldloc.s V_10 + IL_0058: ldc.i4.s 11 + IL_005a: bne.un.s IL_006c + IL_005c: ldloc.s V_11 + IL_005e: ldc.i4.s 12 + IL_0060: bne.un.s IL_006c + IL_0062: ldloc.s V_12 + IL_0064: ldc.i4.s 13 + IL_0066: bne.un.s IL_006c + IL_0068: ldc.i4.1 + IL_0069: stloc.0 + IL_006a: br.s IL_006e + IL_006c: ldc.i4.m1 + IL_006d: stloc.0 + IL_006e: ldloc.0 + IL_006f: call ""void System.Console.WriteLine(int)"" + IL_0074: ret +} +"); + } #endregion } }