提交 42f58dd0 编写于 作者: N Neal Gafter 提交者: GitHub

Fix code gen for pattern switch with constant expression (#19737)

* Fix code gen for pattern switch with constant expression
Fixes #19731

* Add instrumentation for pattern switch with constant expression
Fixes #17090

* Incorrect code for generic pattern switch on a constant expression
Fixes #19734
上级 e34ea6d2
......@@ -308,8 +308,17 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern
if (forType == null)
{
var type = value.Value.Type;
var narrowedExpression = GetBoundPatternMatchingLocal(type);
forType = new DecisionTree.ByValue(narrowedExpression, type.TupleUnderlyingTypeOrSelf(), narrowedExpression.LocalSymbol);
if (byType.Type.Equals(type, TypeCompareKind.AllIgnoreOptions))
{
// reuse the input expression when we have an equivalent type to reduce the number of generated temps
forType = new DecisionTree.ByValue(byType.Expression, type.TupleUnderlyingTypeOrSelf(), null);
}
else
{
var narrowedExpression = GetBoundPatternMatchingLocal(type);
forType = new DecisionTree.ByValue(narrowedExpression, type.TupleUnderlyingTypeOrSelf(), narrowedExpression.LocalSymbol);
}
byType.TypeAndDecision.Add(new KeyValuePair<TypeSymbol, DecisionTree>(type, forType));
}
......
......@@ -60,13 +60,20 @@ private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node)
var expression = _localRewriter.VisitExpression(node.Expression);
var result = ArrayBuilder<BoundStatement>.GetInstance();
if (expression.ConstantValue == null)
// EnC: We need to insert a hidden sequence point to handle function remapping in case
// the containing method is edited while methods invoked in the expression are being executed.
if (!node.WasCompilerGenerated && _localRewriter.Instrument)
{
// EnC: We need to insert a hidden sequence point to handle function remapping in case
// the containing method is edited while methods invoked in the expression are being executed.
if (!node.WasCompilerGenerated && _localRewriter.Instrument)
var instrumentedExpression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory);
if (expression.ConstantValue == null)
{
expression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory);
expression = instrumentedExpression;
}
else
{
// If the expression is a constant, we leave it alone (the decision tree lowering code needs
// to see that constant). But we add an additional leading statement with the instrumented expression.
result.Add(_factory.ExpressionStatement(instrumentedExpression));
}
}
......@@ -172,7 +179,13 @@ private void LowerPatternSwitch(BoundExpression loweredExpression, BoundPatternS
if (defaultLabel != null && !loweredDecisionTree.MatchIsComplete)
{
Add(loweredDecisionTree, (e, t) => new DecisionTree.Guarded(loweredExpression, loweredExpression.Type, default(ImmutableArray<KeyValuePair<BoundExpression, BoundExpression>>), defaultSection, null, defaultLabel));
Add(loweredDecisionTree, (e, t) => new DecisionTree.Guarded(
expression: loweredExpression,
type: loweredExpression.Type,
bindings: default,
sectionSyntax: defaultSection,
guard: null,
label: defaultLabel));
}
// We discard use-site diagnostics, as they have been reported during initial binding.
......@@ -248,61 +261,71 @@ private void LowerDecisionTree(BoundExpression expression, DecisionTree decision
private void LowerDecisionTree(DecisionTree.ByType byType)
{
var inputConstant = byType.Expression.ConstantValue;
// three-valued: true if input known null, false if input known non-null, null if not known.
bool? inputIsNull = null;
if (inputConstant != null)
{
if (inputConstant.IsNull)
{
// input is the constant null
LowerDecisionTree(byType.Expression, byType.WhenNull);
if (byType.WhenNull?.MatchIsComplete != true)
{
LowerDecisionTree(byType.Expression, byType.Default);
}
}
else
inputIsNull = inputConstant.IsNull;
}
var defaultLabel = _factory.GenerateLabel("byTypeDefault");
if (byType.Type.CanContainNull())
{
switch (inputIsNull)
{
// input is a non-null constant
foreach (var kvp in byType.TypeAndDecision)
{
LowerDecisionTree(byType.Expression, kvp.Value);
if (kvp.Value.MatchIsComplete)
case true:
{
return;
// Input is known to be null. Generate code for the null case only.
LowerDecisionTree(byType.Expression, byType.WhenNull);
if (byType.WhenNull?.MatchIsComplete != true)
{
_loweredDecisionTree.Add(_factory.Goto(defaultLabel));
}
break;
}
case false:
{
// Input is known not to be null. Don't generate any code for the null case.
break;
}
case null:
{
// Unknown if the input is null. First test for null
var notNullLabel = _factory.GenerateLabel("notNull");
var inputExpression = byType.Expression;
var objectType = _factory.SpecialType(SpecialType.System_Object);
var nullValue = _factory.Null(objectType);
BoundExpression notNull =
byType.Type.IsNullableType()
? _localRewriter.RewriteNullableNullEquality(
_factory.Syntax,
BinaryOperatorKind.NullableNullNotEqual,
byType.Expression,
nullValue,
_factory.SpecialType(SpecialType.System_Boolean))
: _factory.ObjectNotEqual(nullValue, _factory.Convert(objectType, byType.Expression));
_loweredDecisionTree.Add(_factory.ConditionalGoto(notNull, notNullLabel, true));
LowerDecisionTree(byType.Expression, byType.WhenNull);
if (byType.WhenNull?.MatchIsComplete != true)
{
_loweredDecisionTree.Add(_factory.Goto(defaultLabel));
}
_loweredDecisionTree.Add(_factory.Label(notNullLabel));
break;
}
}
LowerDecisionTree(byType.Expression, byType.Default);
}
}
else
{
var defaultLabel = _factory.GenerateLabel("byTypeDefault");
// input is not a constant
if (byType.Type.CanContainNull())
{
// first test for null
var notNullLabel = _factory.GenerateLabel("notNull");
var inputExpression = byType.Expression;
var objectType = _factory.SpecialType(SpecialType.System_Object);
var nullValue = _factory.Null(objectType);
BoundExpression notNull = byType.Type.IsNullableType()
? _localRewriter.RewriteNullableNullEquality(_factory.Syntax, BinaryOperatorKind.NullableNullNotEqual, byType.Expression, nullValue, _factory.SpecialType(SpecialType.System_Boolean))
: _factory.ObjectNotEqual(nullValue, _factory.Convert(objectType, byType.Expression));
_loweredDecisionTree.Add(_factory.ConditionalGoto(notNull, notNullLabel, true));
LowerDecisionTree(byType.Expression, byType.WhenNull);
if (byType.WhenNull?.MatchIsComplete != true)
{
_loweredDecisionTree.Add(_factory.Goto(defaultLabel));
}
_loweredDecisionTree.Add(_factory.Label(notNullLabel));
}
else
{
Debug.Assert(byType.WhenNull == null);
}
Debug.Assert(byType.WhenNull == null);
}
if (inputIsNull != true)
{
foreach (var td in byType.TypeAndDecision)
{
// then test for each type, sequentially
......@@ -314,11 +337,11 @@ private void LowerDecisionTree(DecisionTree.ByType byType)
LowerDecisionTree(decision.Expression, decision);
_loweredDecisionTree.Add(_factory.Label(failLabel));
}
// finally, the default for when no type matches
_loweredDecisionTree.Add(_factory.Label(defaultLabel));
LowerDecisionTree(byType.Expression, byType.Default);
}
// finally, the default for when no type matches
_loweredDecisionTree.Add(_factory.Label(defaultLabel));
LowerDecisionTree(byType.Expression, byType.Default);
}
private BoundExpression TypeTestAndCopyToTemp(BoundExpression input, BoundExpression temp)
......@@ -363,8 +386,15 @@ private void LowerConstantValueDecision(DecisionTree.ByValue byValue)
var value = byValue.Expression.ConstantValue.Value;
Debug.Assert(value != null);
// because we are switching on a constant, the decision tree builder does not produce a (nonempty) ByValue
Debug.Assert(byValue.ValueAndDecision.Count == 0);
// If there is a matching value among the cases, that is the only one lowered.
if (byValue.ValueAndDecision.TryGetValue(value, out DecisionTree valueDecision))
{
LowerDecisionTree(byValue.Expression, valueDecision);
if (valueDecision.MatchIsComplete)
{
return;
}
}
LowerDecisionTree(byValue.Expression, byValue.Default);
}
......
......@@ -7653,23 +7653,26 @@ .locals init (object V_0)
expectedOutput: "RemoveEmptyEntries");
compVerifier.VerifyIL("Program.Main",
@"{
// Code size 24 (0x18)
// Code size 26 (0x1a)
.maxstack 1
.locals init (object V_0,
object V_1) //o
object V_1, //o
System.StringSplitOptions V_2)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: box ""System.StringSplitOptions""
IL_0007: stloc.0
IL_0008: br.s IL_000a
IL_000a: ldloc.0
IL_000b: stloc.1
IL_000c: br.s IL_000e
IL_000e: ldloc.1
IL_000f: call ""void System.Console.WriteLine(object)""
IL_0014: nop
IL_0015: br.s IL_0017
IL_0017: ret
IL_0002: stloc.2
IL_0003: ldc.i4.1
IL_0004: box ""System.StringSplitOptions""
IL_0009: stloc.0
IL_000a: br.s IL_000c
IL_000c: ldloc.0
IL_000d: stloc.1
IL_000e: br.s IL_0010
IL_0010: ldloc.1
IL_0011: call ""void System.Console.WriteLine(object)""
IL_0016: nop
IL_0017: br.s IL_0019
IL_0019: ret
}"
);
}
......
......@@ -2403,7 +2403,7 @@ static void M()
v0.VerifyIL("C.M", @"
{
// Code size 232 (0xe8)
// Code size 230 (0xe6)
.maxstack 2
.locals init (object V_0,
int V_1,
......@@ -2421,7 +2421,7 @@ .maxstack 2
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: brtrue.s IL_0010
IL_000e: br.s IL_0083
IL_000e: br.s IL_0081
IL_0010: ldloc.0
IL_0011: stloc.s V_8
IL_0013: ldloc.s V_8
......@@ -2440,7 +2440,7 @@ .maxstack 2
IL_002e: ldc.i4.1
IL_002f: beq.s IL_0033
IL_0031: br.s IL_0035
IL_0033: br.s IL_0085
IL_0033: br.s IL_0083
IL_0035: ldloc.0
IL_0036: stloc.s V_8
IL_0038: ldloc.s V_8
......@@ -2454,76 +2454,74 @@ .maxstack 2
IL_0048: ldloc.s V_8
IL_004a: unbox.any ""byte""
IL_004f: stloc.2
IL_0050: brfalse.s IL_005e
IL_0052: br.s IL_0092
IL_0050: brfalse.s IL_005c
IL_0052: br.s IL_0090
IL_0054: ldloc.2
IL_0055: stloc.2
IL_0056: ldloc.2
IL_0057: ldc.i4.1
IL_0058: beq.s IL_005c
IL_005a: br.s IL_005e
IL_005c: br.s IL_00bc
IL_005e: ldloc.0
IL_005f: stloc.s V_8
IL_0061: ldloc.s V_8
IL_0063: isinst ""int""
IL_0068: ldnull
IL_0069: cgt.un
IL_006b: dup
IL_006c: brtrue.s IL_0071
IL_006e: ldc.i4.0
IL_006f: br.s IL_0078
IL_0071: ldloc.s V_8
IL_0073: unbox.any ""int""
IL_0078: stloc.1
IL_0079: brfalse.s IL_007f
IL_007b: br.s IL_00a6
IL_007d: br.s IL_00c9
IL_007f: ldloc.0
IL_0080: stloc.0
IL_0081: br.s IL_00d8
IL_0083: br.s IL_00e7
IL_0085: ldstr ""int 1""
IL_008a: call ""void System.Console.WriteLine(string)""
IL_008f: nop
IL_0090: br.s IL_00e7
IL_0092: ldloc.2
IL_0093: stloc.3
IL_0094: call ""bool C.P()""
IL_0099: brtrue.s IL_009d
IL_009b: br.s IL_0054
IL_009d: ldloc.3
IL_009e: call ""void System.Console.WriteLine(int)""
IL_00a3: nop
IL_00a4: br.s IL_00e7
IL_00a6: ldloc.1
IL_00a7: stloc.s V_4
IL_00a9: call ""bool C.P()""
IL_00ae: brtrue.s IL_00b2
IL_00b0: br.s IL_007d
IL_00b2: ldloc.s V_4
IL_00b4: call ""void System.Console.WriteLine(int)""
IL_00b9: nop
IL_00ba: br.s IL_00e7
IL_00bc: ldstr ""byte 1""
IL_00c1: call ""void System.Console.WriteLine(string)""
IL_00c6: nop
IL_00c7: br.s IL_00e7
IL_00c9: ldloc.1
IL_00ca: stloc.s V_5
IL_00cc: br.s IL_00ce
IL_00ce: ldloc.s V_5
IL_00d0: call ""void System.Console.WriteLine(int)""
IL_00d5: nop
IL_00d6: br.s IL_00e7
IL_00d8: ldloc.0
IL_00d9: stloc.s V_6
IL_00db: br.s IL_00dd
IL_00dd: ldloc.s V_6
IL_00df: call ""void System.Console.WriteLine(object)""
IL_00e4: nop
IL_00e5: br.s IL_00e7
IL_00e7: ret
IL_0055: ldc.i4.1
IL_0056: beq.s IL_005a
IL_0058: br.s IL_005c
IL_005a: br.s IL_00ba
IL_005c: ldloc.0
IL_005d: stloc.s V_8
IL_005f: ldloc.s V_8
IL_0061: isinst ""int""
IL_0066: ldnull
IL_0067: cgt.un
IL_0069: dup
IL_006a: brtrue.s IL_006f
IL_006c: ldc.i4.0
IL_006d: br.s IL_0076
IL_006f: ldloc.s V_8
IL_0071: unbox.any ""int""
IL_0076: stloc.1
IL_0077: brfalse.s IL_007d
IL_0079: br.s IL_00a4
IL_007b: br.s IL_00c7
IL_007d: ldloc.0
IL_007e: stloc.0
IL_007f: br.s IL_00d6
IL_0081: br.s IL_00e5
IL_0083: ldstr ""int 1""
IL_0088: call ""void System.Console.WriteLine(string)""
IL_008d: nop
IL_008e: br.s IL_00e5
IL_0090: ldloc.2
IL_0091: stloc.3
IL_0092: call ""bool C.P()""
IL_0097: brtrue.s IL_009b
IL_0099: br.s IL_0054
IL_009b: ldloc.3
IL_009c: call ""void System.Console.WriteLine(int)""
IL_00a1: nop
IL_00a2: br.s IL_00e5
IL_00a4: ldloc.1
IL_00a5: stloc.s V_4
IL_00a7: call ""bool C.P()""
IL_00ac: brtrue.s IL_00b0
IL_00ae: br.s IL_007b
IL_00b0: ldloc.s V_4
IL_00b2: call ""void System.Console.WriteLine(int)""
IL_00b7: nop
IL_00b8: br.s IL_00e5
IL_00ba: ldstr ""byte 1""
IL_00bf: call ""void System.Console.WriteLine(string)""
IL_00c4: nop
IL_00c5: br.s IL_00e5
IL_00c7: ldloc.1
IL_00c8: stloc.s V_5
IL_00ca: br.s IL_00cc
IL_00cc: ldloc.s V_5
IL_00ce: call ""void System.Console.WriteLine(int)""
IL_00d3: nop
IL_00d4: br.s IL_00e5
IL_00d6: ldloc.0
IL_00d7: stloc.s V_6
IL_00d9: br.s IL_00db
IL_00db: ldloc.s V_6
IL_00dd: call ""void System.Console.WriteLine(object)""
IL_00e2: nop
IL_00e3: br.s IL_00e5
IL_00e5: ret
}");
var methodData0 = v0.TestData.GetMethodData("C.M");
var method0 = compilation0.GetMember<MethodSymbol>("C.M");
......@@ -2536,7 +2534,7 @@ .maxstack 2
diff1.VerifyIL("C.M", @"
{
// Code size 232 (0xe8)
// Code size 230 (0xe6)
.maxstack 2
.locals init (object V_0,
int V_1,
......@@ -2555,7 +2553,7 @@ .maxstack 2
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: brtrue.s IL_0010
IL_000e: br.s IL_0083
IL_000e: br.s IL_0081
IL_0010: ldloc.0
IL_0011: stloc.s V_9
IL_0013: ldloc.s V_9
......@@ -2574,7 +2572,7 @@ .maxstack 2
IL_002e: ldc.i4.1
IL_002f: beq.s IL_0033
IL_0031: br.s IL_0035
IL_0033: br.s IL_0085
IL_0033: br.s IL_0083
IL_0035: ldloc.0
IL_0036: stloc.s V_9
IL_0038: ldloc.s V_9
......@@ -2588,76 +2586,74 @@ .maxstack 2
IL_0048: ldloc.s V_9
IL_004a: unbox.any ""byte""
IL_004f: stloc.2
IL_0050: brfalse.s IL_005e
IL_0052: br.s IL_0092
IL_0050: brfalse.s IL_005c
IL_0052: br.s IL_0090
IL_0054: ldloc.2
IL_0055: stloc.2
IL_0056: ldloc.2
IL_0057: ldc.i4.1
IL_0058: beq.s IL_005c
IL_005a: br.s IL_005e
IL_005c: br.s IL_00bc
IL_005e: ldloc.0
IL_005f: stloc.s V_9
IL_0061: ldloc.s V_9
IL_0063: isinst ""int""
IL_0068: ldnull
IL_0069: cgt.un
IL_006b: dup
IL_006c: brtrue.s IL_0071
IL_006e: ldc.i4.0
IL_006f: br.s IL_0078
IL_0071: ldloc.s V_9
IL_0073: unbox.any ""int""
IL_0078: stloc.1
IL_0079: brfalse.s IL_007f
IL_007b: br.s IL_00a6
IL_007d: br.s IL_00c9
IL_007f: ldloc.0
IL_0080: stloc.0
IL_0081: br.s IL_00d8
IL_0083: br.s IL_00e7
IL_0085: ldstr ""int 1""
IL_008a: call ""void System.Console.WriteLine(string)""
IL_008f: nop
IL_0090: br.s IL_00e7
IL_0092: ldloc.2
IL_0093: stloc.3
IL_0094: call ""bool C.P()""
IL_0099: brtrue.s IL_009d
IL_009b: br.s IL_0054
IL_009d: ldloc.3
IL_009e: call ""void System.Console.WriteLine(int)""
IL_00a3: nop
IL_00a4: br.s IL_00e7
IL_00a6: ldloc.1
IL_00a7: stloc.s V_4
IL_00a9: call ""bool C.P()""
IL_00ae: brtrue.s IL_00b2
IL_00b0: br.s IL_007d
IL_00b2: ldloc.s V_4
IL_00b4: call ""void System.Console.WriteLine(int)""
IL_00b9: nop
IL_00ba: br.s IL_00e7
IL_00bc: ldstr ""byte 1""
IL_00c1: call ""void System.Console.WriteLine(string)""
IL_00c6: nop
IL_00c7: br.s IL_00e7
IL_00c9: ldloc.1
IL_00ca: stloc.s V_5
IL_00cc: br.s IL_00ce
IL_00ce: ldloc.s V_5
IL_00d0: call ""void System.Console.WriteLine(int)""
IL_00d5: nop
IL_00d6: br.s IL_00e7
IL_00d8: ldloc.0
IL_00d9: stloc.s V_6
IL_00db: br.s IL_00dd
IL_00dd: ldloc.s V_6
IL_00df: call ""void System.Console.WriteLine(object)""
IL_00e4: nop
IL_00e5: br.s IL_00e7
IL_00e7: ret
IL_0055: ldc.i4.1
IL_0056: beq.s IL_005a
IL_0058: br.s IL_005c
IL_005a: br.s IL_00ba
IL_005c: ldloc.0
IL_005d: stloc.s V_9
IL_005f: ldloc.s V_9
IL_0061: isinst ""int""
IL_0066: ldnull
IL_0067: cgt.un
IL_0069: dup
IL_006a: brtrue.s IL_006f
IL_006c: ldc.i4.0
IL_006d: br.s IL_0076
IL_006f: ldloc.s V_9
IL_0071: unbox.any ""int""
IL_0076: stloc.1
IL_0077: brfalse.s IL_007d
IL_0079: br.s IL_00a4
IL_007b: br.s IL_00c7
IL_007d: ldloc.0
IL_007e: stloc.0
IL_007f: br.s IL_00d6
IL_0081: br.s IL_00e5
IL_0083: ldstr ""int 1""
IL_0088: call ""void System.Console.WriteLine(string)""
IL_008d: nop
IL_008e: br.s IL_00e5
IL_0090: ldloc.2
IL_0091: stloc.3
IL_0092: call ""bool C.P()""
IL_0097: brtrue.s IL_009b
IL_0099: br.s IL_0054
IL_009b: ldloc.3
IL_009c: call ""void System.Console.WriteLine(int)""
IL_00a1: nop
IL_00a2: br.s IL_00e5
IL_00a4: ldloc.1
IL_00a5: stloc.s V_4
IL_00a7: call ""bool C.P()""
IL_00ac: brtrue.s IL_00b0
IL_00ae: br.s IL_007b
IL_00b0: ldloc.s V_4
IL_00b2: call ""void System.Console.WriteLine(int)""
IL_00b7: nop
IL_00b8: br.s IL_00e5
IL_00ba: ldstr ""byte 1""
IL_00bf: call ""void System.Console.WriteLine(string)""
IL_00c4: nop
IL_00c5: br.s IL_00e5
IL_00c7: ldloc.1
IL_00c8: stloc.s V_5
IL_00ca: br.s IL_00cc
IL_00cc: ldloc.s V_5
IL_00ce: call ""void System.Console.WriteLine(int)""
IL_00d3: nop
IL_00d4: br.s IL_00e5
IL_00d6: ldloc.0
IL_00d7: stloc.s V_6
IL_00d9: br.s IL_00db
IL_00db: ldloc.s V_6
IL_00dd: call ""void System.Console.WriteLine(object)""
IL_00e2: nop
IL_00e3: br.s IL_00e5
IL_00e5: ret
}");
}
......
......@@ -2984,6 +2984,436 @@ class Student : Person { public double GPA; }
</symbols>");
}
[Fact, WorkItem(17090, "https://github.com/dotnet/roslyn/issues/17090"), WorkItem(19731, "https://github.com/dotnet/roslyn/issues/19731")]
public void SwitchWithConstantPattern()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
M1();
M2();
}
static void M1()
{
switch
(1)
{
case 0 when true:
;
case 1:
Console.Write(1);
break;
case 2:
;
}
}
static void M2()
{
switch
(nameof(M2))
{
case nameof(M1) when true:
;
case nameof(M2):
Console.Write(nameof(M2));
break;
case nameof(Main):
;
}
}
}
";
var c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugExe);
c.VerifyDiagnostics();
var verifier = CompileAndVerify(c, expectedOutput: "1M2");
verifier.VerifyIL(qualifiedMethodName: "Program.M1", sequencePoints: "Program.M1", source: source,
expectedIL: @"{
// Code size 15 (0xf)
.maxstack 1
.locals init (int V_0)
// sequence point: {
IL_0000: nop
// sequence point: switch ... (1
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
// sequence point: Console.Write(1);
IL_0005: ldc.i4.1
IL_0006: call ""void System.Console.Write(int)""
IL_000b: nop
// sequence point: break;
IL_000c: br.s IL_000e
// sequence point: }
IL_000e: ret
}");
verifier.VerifyIL(qualifiedMethodName: "Program.M2", sequencePoints: "Program.M2", source: source,
expectedIL: @"{
// Code size 23 (0x17)
.maxstack 1
.locals init (string V_0)
// sequence point: {
IL_0000: nop
// sequence point: switch ... (nameof(M2)
IL_0001: ldstr ""M2""
IL_0006: stloc.0
IL_0007: br.s IL_0009
// sequence point: Console.Write(nameof(M2));
IL_0009: ldstr ""M2""
IL_000e: call ""void System.Console.Write(string)""
IL_0013: nop
// sequence point: break;
IL_0014: br.s IL_0016
// sequence point: }
IL_0016: ret
}");
// Check the release code generation too.
c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.ReleaseExe);
c.VerifyDiagnostics();
verifier = CompileAndVerify(c, expectedOutput: "1M2");
verifier.VerifyIL("Program.M1",
@"{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.1
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}");
verifier.VerifyIL("Program.M2",
@"{
// Code size 11 (0xb)
.maxstack 1
IL_0000: ldstr ""M2""
IL_0005: call ""void System.Console.Write(string)""
IL_000a: ret
}");
}
[Fact, WorkItem(19734, "https://github.com/dotnet/roslyn/issues/19734")]
public void SwitchWithConstantGenericPattern_01()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
M1<int>(); // 1
M1<long>(); // 2
M2<string>(); // 3
M2<int>(); // 4
}
static void M1<T>()
{
switch (1)
{
case T t:
Console.Write(1);
break;
case int i:
Console.Write(2);
break;
}
}
static void M2<T>()
{
switch (nameof(M2))
{
case T t:
Console.Write(3);
break;
case string s:
Console.Write(4);
break;
case null:
;
}
}
}
";
var c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular7_1);
c.VerifyDiagnostics();
var verifier = CompileAndVerify(c, expectedOutput: "1234");
verifier.VerifyIL(qualifiedMethodName: "Program.M1<T>", sequencePoints: "Program.M1", source: source,
expectedIL: @"{
// Code size 80 (0x50)
.maxstack 2
.locals init (T V_0,
int V_1,
T V_2, //t
int V_3, //i
int V_4,
object V_5,
T V_6)
// sequence point: {
IL_0000: nop
// sequence point: switch (1)
IL_0001: ldc.i4.1
IL_0002: stloc.s V_4
IL_0004: ldc.i4.1
IL_0005: box ""int""
IL_000a: stloc.s V_5
IL_000c: ldloc.s V_5
IL_000e: isinst ""T""
IL_0013: ldnull
IL_0014: cgt.un
IL_0016: dup
IL_0017: brtrue.s IL_0025
IL_0019: ldloca.s V_6
IL_001b: initobj ""T""
IL_0021: ldloc.s V_6
IL_0023: br.s IL_002c
IL_0025: ldloc.s V_5
IL_0027: unbox.any ""T""
IL_002c: stloc.0
IL_002d: brfalse.s IL_0031
IL_002f: br.s IL_0035
IL_0031: ldc.i4.1
IL_0032: stloc.1
IL_0033: br.s IL_0042
// sequence point: <hidden>
IL_0035: ldloc.0
IL_0036: stloc.2
IL_0037: br.s IL_0039
// sequence point: Console.Write(1);
IL_0039: ldc.i4.1
IL_003a: call ""void System.Console.Write(int)""
IL_003f: nop
// sequence point: break;
IL_0040: br.s IL_004f
// sequence point: <hidden>
IL_0042: ldloc.1
IL_0043: stloc.3
IL_0044: br.s IL_0046
// sequence point: Console.Write(2);
IL_0046: ldc.i4.2
IL_0047: call ""void System.Console.Write(int)""
IL_004c: nop
// sequence point: break;
IL_004d: br.s IL_004f
// sequence point: }
IL_004f: ret
}");
verifier.VerifyIL(qualifiedMethodName: "Program.M2<T>", sequencePoints: "Program.M2", source: source,
expectedIL: @"{
// Code size 87 (0x57)
.maxstack 2
.locals init (T V_0,
string V_1,
T V_2, //t
string V_3, //s
string V_4,
object V_5,
T V_6)
// sequence point: {
IL_0000: nop
// sequence point: switch (nameof(M2))
IL_0001: ldstr ""M2""
IL_0006: stloc.s V_4
IL_0008: ldstr ""M2""
IL_000d: stloc.s V_5
IL_000f: ldloc.s V_5
IL_0011: isinst ""T""
IL_0016: ldnull
IL_0017: cgt.un
IL_0019: dup
IL_001a: brtrue.s IL_0028
IL_001c: ldloca.s V_6
IL_001e: initobj ""T""
IL_0024: ldloc.s V_6
IL_0026: br.s IL_002f
IL_0028: ldloc.s V_5
IL_002a: unbox.any ""T""
IL_002f: stloc.0
IL_0030: brfalse.s IL_0034
IL_0032: br.s IL_003c
IL_0034: ldstr ""M2""
IL_0039: stloc.1
IL_003a: br.s IL_0049
// sequence point: <hidden>
IL_003c: ldloc.0
IL_003d: stloc.2
IL_003e: br.s IL_0040
// sequence point: Console.Write(3);
IL_0040: ldc.i4.3
IL_0041: call ""void System.Console.Write(int)""
IL_0046: nop
// sequence point: break;
IL_0047: br.s IL_0056
// sequence point: <hidden>
IL_0049: ldloc.1
IL_004a: stloc.3
IL_004b: br.s IL_004d
// sequence point: Console.Write(4);
IL_004d: ldc.i4.4
IL_004e: call ""void System.Console.Write(int)""
IL_0053: nop
// sequence point: break;
IL_0054: br.s IL_0056
// sequence point: }
IL_0056: ret
}");
// Check the release code generation too.
c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1);
c.VerifyDiagnostics();
verifier = CompileAndVerify(c, expectedOutput: "1234");
verifier.VerifyIL("Program.M1<T>",
@"{
// Code size 57 (0x39)
.maxstack 2
.locals init (T V_0,
int V_1,
object V_2,
T V_3)
IL_0000: ldc.i4.1
IL_0001: box ""int""
IL_0006: stloc.2
IL_0007: ldloc.2
IL_0008: isinst ""T""
IL_000d: ldnull
IL_000e: cgt.un
IL_0010: dup
IL_0011: brtrue.s IL_001e
IL_0013: ldloca.s V_3
IL_0015: initobj ""T""
IL_001b: ldloc.3
IL_001c: br.s IL_0024
IL_001e: ldloc.2
IL_001f: unbox.any ""T""
IL_0024: stloc.0
IL_0025: brtrue.s IL_002b
IL_0027: ldc.i4.1
IL_0028: stloc.1
IL_0029: br.s IL_0032
IL_002b: ldc.i4.1
IL_002c: call ""void System.Console.Write(int)""
IL_0031: ret
IL_0032: ldc.i4.2
IL_0033: call ""void System.Console.Write(int)""
IL_0038: ret
}");
verifier.VerifyIL("Program.M2<T>",
@"{
// Code size 60 (0x3c)
.maxstack 2
.locals init (T V_0,
string V_1,
object V_2,
T V_3)
IL_0000: ldstr ""M2""
IL_0005: stloc.2
IL_0006: ldloc.2
IL_0007: isinst ""T""
IL_000c: ldnull
IL_000d: cgt.un
IL_000f: dup
IL_0010: brtrue.s IL_001d
IL_0012: ldloca.s V_3
IL_0014: initobj ""T""
IL_001a: ldloc.3
IL_001b: br.s IL_0023
IL_001d: ldloc.2
IL_001e: unbox.any ""T""
IL_0023: stloc.0
IL_0024: brtrue.s IL_002e
IL_0026: ldstr ""M2""
IL_002b: stloc.1
IL_002c: br.s IL_0035
IL_002e: ldc.i4.3
IL_002f: call ""void System.Console.Write(int)""
IL_0034: ret
IL_0035: ldc.i4.4
IL_0036: call ""void System.Console.Write(int)""
IL_003b: ret
}");
}
[Fact, WorkItem(19734, "https://github.com/dotnet/roslyn/issues/19734")]
public void SwitchWithConstantGenericPattern_02()
{
string source = @"
using System;
class Program
{
static void Main(string[] args)
{
M2<string>(); // 6
M2<int>(); // 6
}
static void M2<T>()
{
const string x = null;
switch (x)
{
case T t:
;
case string s:
;
case null:
Console.Write(6);
break;
}
}
}
";
var c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular7_1);
c.VerifyDiagnostics();
var verifier = CompileAndVerify(c, expectedOutput: "66");
verifier.VerifyIL(qualifiedMethodName: "Program.M2<T>", sequencePoints: "Program.M2", source: source,
expectedIL: @"{
// Code size 15 (0xf)
.maxstack 1
.locals init (T V_0, //t
string V_1, //s
string V_2)
// sequence point: {
IL_0000: nop
// sequence point: switch (x)
IL_0001: ldnull
IL_0002: stloc.2
IL_0003: br.s IL_0005
// sequence point: Console.Write(6);
IL_0005: ldc.i4.6
IL_0006: call ""void System.Console.Write(int)""
IL_000b: nop
// sequence point: break;
IL_000c: br.s IL_000e
// sequence point: }
IL_000e: ret
}");
// Check the release code generation too.
c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1);
c.VerifyDiagnostics();
verifier = CompileAndVerify(c, expectedOutput: "66");
verifier.VerifyIL("Program.M2<T>",
@"{
// Code size 7 (0x7)
.maxstack 1
IL_0000: ldc.i4.6
IL_0001: call ""void System.Console.Write(int)""
IL_0006: ret
}");
}
#endregion
#region DoStatement
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册