-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Suboptimal code for type pattern matching in a switch statement #22654
Comments
Another thing: bool isInt = o is int;
int num = isInt ? ((int)o) : 0;
if (isInt && whenCondition) The cast |
In this case, if there is more than one type pattern with the same type the generated code will unbox the value more than once. |
Tagging @gafter |
The pattern-matching lowering code is being rewritten from scratch (to support recursive patterns, too). I expect most of the improvements you seek here will come for "free" in the new code. But it will be some time before that rewrite is ready for prime time.
No, the |
I am adding the following test to the verify that this will be fixed in C# 8.0. [Fact, WorkItem(22654, "https://github.com/dotnet/roslyn/issues/22654")]
public void NoRedundantTypeCheck()
{
var source =
@"using System;
public class C
{
public void SwitchBasedPatternMatching(object o)
{
switch (o)
{
case int n when n == 1:
Console.WriteLine(""1""); break;
case string s:
Console.WriteLine(""s""); break;
case int n when n == 2:
Console.WriteLine(""2""); break;
}
}
}";
var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll);
compilation.VerifyDiagnostics();
var compVerifier = CompileAndVerify(compilation);
compVerifier.VerifyIL("C.SwitchBasedPatternMatching",
@"{
// Code size 77 (0x4d)
.maxstack 2
.locals init (object V_0,
int V_1,
string V_2)
IL_0000: ldarg.1
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: isinst ""int""
IL_0008: brfalse.s IL_0013
IL_000a: ldloc.0
IL_000b: unbox.any ""int""
IL_0010: stloc.1
IL_0011: br.s IL_0024
IL_0013: ldloc.0
IL_0014: isinst ""string""
IL_0019: brfalse.s IL_004c
IL_001b: ldloc.0
IL_001c: castclass ""string""
IL_0021: stloc.2
IL_0022: br.s IL_0033
IL_0024: ldloc.1
IL_0025: ldc.i4.1
IL_0026: bne.un.s IL_003e
IL_0028: ldstr ""1""
IL_002d: call ""void System.Console.WriteLine(string)""
IL_0032: ret
IL_0033: ldstr ""s""
IL_0038: call ""void System.Console.WriteLine(string)""
IL_003d: ret
IL_003e: ldloc.1
IL_003f: ldc.i4.2
IL_0040: bne.un.s IL_004c
IL_0042: ldstr ""2""
IL_0047: call ""void System.Console.WriteLine(string)""
IL_004c: ret
}");
} |
Test added to confirm fixed in #25567 |
The compiler is smart enough to group different clauses with the same type together:
The generated code roughly looks like this:
If the switch statement has more than one consecutive type patterns with the same type the compiler will check the type only once and will check different predicates inside the if statement. In the case above it means that only one unboxing operation will happen.
But if the cases clauses intermixed with each other, then the code is not that optimal:
In this case, the generated code checks that
o
isint
two times:It is clear that the order of the case clauses matters know. But I can't see why the compiler can't group different cases together based on a type and generate effectively the same code as for the case before.
Is there a use case when this optimization will have an observable side effect or there is a room for optimization here?
The text was updated successfully, but these errors were encountered: