-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix long tuples in PatternLocalRewriter #47893
Changes from all commits
995012f
e281d60
3ed21a1
c921f92
d31ab2b
de76513
33f4e5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<BoundExpression>.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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by the change to get rid of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @RikkiGibson I was expecting to see some test failures related to this too, but I don't know what scenarios exactly that should fail. Do you know any scenarios that should fail to add tests for them? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried the following scenario to induce a failure but it continued to have the expected side-effects in your branch: [Fact, WorkItem(47878, "https://github.com/dotnet/roslyn/issues/47878")]
public void VerifyIL_8ElementsTuple_SideEffects()
{
var text = @"
using System;
public class C
{
public static void Main()
{
int x = (M(1), M(2), M(3), M(4), M(5), M(6), M(7), M(8)) switch
{
(1, 2, 3, 4, 5, 6, 7, 7) => 1,
(1, 2, 3, 4, 5, 6, 7, 8) => 1,
_ => -1,
};
Console.WriteLine(x);
}
static int M(int x)
{
Console.Write(x);
return x;
}
}
";
CompileAndVerify(text, expectedOutput: "123456781");
} I think the next thing I would try would be to make many of the tuple components local variables and see if the lowering code reuses the local variables properly instead of introducing new ones. I'll try to debug in more detail soon. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's the scenario where we have unexpected side effects: [Fact, WorkItem(47878, "https://github.com/dotnet/roslyn/issues/47878")]
public void VerifyIL_8ElementsTuple_SideEffects_02()
{
var text = @"
using System;
using System.Runtime.CompilerServices;
public class C
{
public static void Main()
{
try
{
int x = (M(1), M(2), M(3), M(4), M(5), M(6), M(7), M(8)) switch
{
(1, 2, 3, 4, 5, 6, 7, 7) => 1,
};
Console.WriteLine(x);
}
catch (SwitchExpressionException ex)
{
Console.Write(""💥"");
}
}
static int M(int x)
{
Console.Write(x);
return x;
}
}
namespace System.Runtime.CompilerServices
{
public class SwitchExpressionException : InvalidOperationException
{
public SwitchExpressionException() {}
public SwitchExpressionException(object unmatchedValue) => UnmatchedValue = unmatchedValue;
public object UnmatchedValue { get; }
}
}
";
CompileAndVerify(text, expectedOutput: "12345678💥");
} With your change, the actual output is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @RikkiGibson That will get us to the assertion failure too. I'm still not sure what is the appropriate fix. I'll add this test and give it a try again, but not sure if I would be able to get the correct fix. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the problem is that when we "unwrap" the tuple elements as is done in this PR, the length of the arguments no longer matches the length of the arguments ot the top-level object creation node. i.e. I can try and look a bit to see how to do this--the fix isn't coming to mind instantly. But to get the behavior of the scenario fixed in 16.8 we may want to take #48028 and then optimize it later. |
||
loweredInput.Expanded, loweredInput.ArgsToParamsOpt, loweredInput.ConstantValueOpt, | ||
loweredInput.InitializerExpressionOpt, loweredInput.BinderOpt, loweredInput.Type); | ||
|
||
return rewrittenDag; | ||
|
||
static BoundExpression getExpression(int index, BoundObjectCreationExpression expression) | ||
jcouv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
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)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the IL for this test and the next test correct? |
||
{ | ||
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 | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, we can't just access Arguments[i] for two reasons: