未验证 提交 5739c71b 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Fix EnC debug information emitted for patterns (#37239)

* Fix EnC debug information emitted for patterns

Update calculation of syntax offset to account for a new case when a node (a switch expression) that is associated with a variable, closure or lambda may share start offset with other node of the same kind (`expr switch { … } switch { … }`). Use the offset of the `switch` keyword instead of the starting offset of the expression to disambiguate.

Assign ordinals to variables synthesized for storing pattern values across cases. This is required to support complex patterns since we can no longer rely on the type of these variables to be distinct. This will require follow up in the IDE to disallow updating/adding/reordering the case clauses of switch expression which there an active statement is present within the switch statement. If the cases are unmodified the compiler guarantees that the order in which the synthesized variables are generated remains the same, so we can map the variables using their ordinal.

Mark all variables synthesized during lowering of switch expression as short-lived. Their lifespan is limited to the switch expression, which does not include a sequence point.

Disallow editing methods that contain switch expression. This is necessary until bugs https://github.com/dotnet/roslyn/issues/37232, https://github.com/dotnet/roslyn/issues/37237 are fixed.

* Feedback

* Update tests
上级 c4ec3baf
......@@ -157,7 +157,7 @@ private LocalDefinition LazyReturnTemp
var bodySyntax = _methodBodySyntaxOpt;
if (_ilEmitStyle == ILEmitStyle.Debug && bodySyntax != null)
{
int syntaxOffset = _method.CalculateLocalSyntaxOffset(bodySyntax.SpanStart, bodySyntax.SyntaxTree);
int syntaxOffset = _method.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(bodySyntax), bodySyntax.SyntaxTree);
var localSymbol = new SynthesizedLocal(_method, _method.ReturnTypeWithAnnotations, SynthesizedLocalKind.FunctionReturnValue, bodySyntax);
result = _builder.LocalSlotManager.DeclareLocal(
......
......@@ -1435,13 +1435,8 @@ private string GetLocalDebugName(ILocalSymbolInternal local, out LocalDebugId lo
if (_ilEmitStyle == ILEmitStyle.Debug)
{
var syntax = local.GetDeclaratorSyntax();
int syntaxOffset = _method.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree);
// Synthesized locals emitted for switch case patterns are all associated with the switch statement
// and have distinct types. We use their types to match them, not the ordinal as the ordinal might
// change if switch cases are reordered.
int ordinal = (localKind != SynthesizedLocalKind.SwitchCasePatternMatching) ?
_synthesizedLocalOrdinals.AssignLocalOrdinal(localKind, syntaxOffset) : 0;
int syntaxOffset = _method.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(syntax), syntax.SyntaxTree);
int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(localKind, syntaxOffset);
// user-defined locals should have 0 ordinal:
Debug.Assert(ordinal == 0 || localKind != SynthesizedLocalKind.UserDefined);
......
......@@ -13,16 +13,12 @@ private CSharpLambdaSyntaxFacts()
}
public override SyntaxNode GetLambda(SyntaxNode lambdaOrLambdaBodySyntax)
{
return LambdaUtilities.GetLambda(lambdaOrLambdaBodySyntax);
}
=> LambdaUtilities.GetLambda(lambdaOrLambdaBodySyntax);
public override SyntaxNode TryGetCorrespondingLambdaBody(
SyntaxNode previousLambdaSyntax,
SyntaxNode lambdaOrLambdaBodySyntax)
{
return LambdaUtilities.TryGetCorrespondingLambdaBody(
lambdaOrLambdaBodySyntax, previousLambdaSyntax);
}
public override SyntaxNode TryGetCorrespondingLambdaBody(SyntaxNode previousLambdaSyntax, SyntaxNode lambdaOrLambdaBodySyntax)
=> LambdaUtilities.TryGetCorrespondingLambdaBody(lambdaOrLambdaBodySyntax, previousLambdaSyntax);
public override int GetDeclaratorPosition(SyntaxNode node)
=> LambdaUtilities.GetDeclaratorPosition(node);
}
}
......@@ -535,7 +535,7 @@ internal DebugId GetClosureId(SyntaxNode syntax, ArrayBuilder<ClosureDebugInfo>
closureId = new DebugId(closureDebugInfo.Count, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
}
int syntaxOffset = _topLevelMethod.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree);
int syntaxOffset = _topLevelMethod.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(syntax), syntax.SyntaxTree);
closureDebugInfo.Add(new ClosureDebugInfo(syntaxOffset, closureId));
return closureId;
......
......@@ -1388,7 +1388,7 @@ private DebugId GetLambdaId(SyntaxNode syntax, ClosureKind closureKind, int clos
lambdaId = new DebugId(_lambdaDebugInfoBuilder.Count, CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
}
int syntaxOffset = _topLevelMethod.CalculateLocalSyntaxOffset(lambdaOrLambdaBodySyntax.SpanStart, lambdaOrLambdaBodySyntax.SyntaxTree);
int syntaxOffset = _topLevelMethod.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(lambdaOrLambdaBodySyntax), lambdaOrLambdaBodySyntax.SyntaxTree);
_lambdaDebugInfoBuilder.Add(new LambdaDebugInfo(syntaxOffset, lambdaId, closureOrdinal));
return lambdaId;
}
......
......@@ -17,7 +17,7 @@ internal partial class LocalRewriter
/// <summary>
/// A common base class for lowering the pattern switch statement and the pattern switch expression.
/// </summary>
private class BaseSwitchLocalRewriter : PatternLocalRewriter
private abstract class BaseSwitchLocalRewriter : PatternLocalRewriter
{
/// <summary>
/// Map from switch section's syntax to the lowered code for the section. The code for a section
......@@ -37,27 +37,19 @@ private class BaseSwitchLocalRewriter : PatternLocalRewriter
/// </summary>
private readonly PooledDictionary<BoundDecisionDagNode, LabelSymbol> _dagNodeLabels = PooledDictionary<BoundDecisionDagNode, LabelSymbol>.GetInstance();
/// <summary>
/// True if we are translating a switch statement. This affects sequence points (a when clause gets
/// a sequence point in a switch statement, but not in a switch expression).
/// </summary>
private readonly bool _isSwitchStatement;
protected BaseSwitchLocalRewriter(
SyntaxNode node,
LocalRewriter localRewriter,
ImmutableArray<SyntaxNode> arms,
bool isSwitchStatement)
ImmutableArray<SyntaxNode> arms)
: base(node, localRewriter)
{
this._isSwitchStatement = isSwitchStatement;
foreach (var arm in arms)
{
var armBuilder = ArrayBuilder<BoundStatement>.GetInstance();
// We start each switch block of a switch statement with a hidden sequence point so that
// we do not appear to be in the previous switch block when we begin.
if (isSwitchStatement)
if (IsSwitchStatement)
armBuilder.Add(_factory.HiddenSequencePoint());
_switchArms.Add(arm, armBuilder);
......@@ -289,7 +281,7 @@ public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess no
// In a switch statement, there is a hidden sequence point after evaluating the input at the start of
// the code to handle the decision dag. This is necessary so that jumps back from a `when` clause into
// the decision dag do not appear to jump back up to the enclosing construct.
if (_isSwitchStatement)
if (IsSwitchStatement)
result.Add(_factory.HiddenSequencePoint());
return decisionDag;
......@@ -303,9 +295,9 @@ public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess no
Debug.Assert(this._loweredDecisionDag.IsEmpty());
ComputeLabelSet(decisionDag);
LowerDecisionDagCore(decisionDag);
ImmutableArray<BoundStatement> loweredDag = this._loweredDecisionDag.ToImmutableAndFree();
ImmutableDictionary<SyntaxNode, ImmutableArray<BoundStatement>> switchSections = this._switchArms.ToImmutableDictionary(kv => kv.Key, kv => kv.Value.ToImmutableAndFree());
this._switchArms.Clear();
ImmutableArray<BoundStatement> loweredDag = _loweredDecisionDag.ToImmutableAndFree();
var switchSections = _switchArms.ToImmutableDictionary(kv => kv.Key, kv => kv.Value.ToImmutableAndFree());
_switchArms.Clear();
return (loweredDag, switchSections);
}
......@@ -343,7 +335,7 @@ private void LowerDecisionDagCore(BoundDecisionDag decisionDag)
continue;
}
if (this._dagNodeLabels.TryGetValue(node, out LabelSymbol label))
if (_dagNodeLabels.TryGetValue(node, out LabelSymbol label))
{
_loweredDecisionDag.Add(_factory.Label(label));
}
......@@ -581,7 +573,7 @@ private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
BoundStatement conditionalGoto = _factory.ConditionalGoto(_localRewriter.VisitExpression(whenClause.WhenExpression), trueLabel, jumpIfTrue: true);
// Only add instrumentation (such as a sequence point) if the node is not compiler-generated.
if (_isSwitchStatement && !whenClause.WhenExpression.WasCompilerGenerated && _localRewriter.Instrument)
if (IsSwitchStatement && !whenClause.WhenExpression.WasCompilerGenerated && _localRewriter.Instrument)
{
conditionalGoto = _localRewriter._instrumenter.InstrumentSwitchWhenClauseConditionalGotoBody(whenClause.WhenExpression, conditionalGoto);
}
......@@ -592,7 +584,7 @@ private void LowerWhenClause(BoundWhenDecisionDagNode whenClause)
// We hide the jump back into the decision dag, as it is not logically part of the when clause
BoundStatement jump = _factory.Goto(GetDagNodeLabel(whenFalse));
sectionBuilder.Add(_isSwitchStatement ? _factory.HiddenSequencePoint(jump) : jump);
sectionBuilder.Add(IsSwitchStatement ? _factory.HiddenSequencePoint(jump) : jump);
}
else
{
......@@ -618,7 +610,7 @@ private void LowerDecisionDagNode(BoundDecisionDagNode node, BoundDecisionDagNod
// We add a hidden sequence point after the evaluation's side-effect, which may be a call out
// to user code such as `Deconstruct` or a property get, to permit edit-and-continue to
// synchronize on changes.
if (_isSwitchStatement)
if (IsSwitchStatement)
_loweredDecisionDag.Add(_factory.HiddenSequencePoint());
if (nextNode != evaluationNode.Next)
......
......@@ -18,7 +18,7 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node
return result;
}
private class IsPatternExpressionLocalRewriter : PatternLocalRewriter
private sealed class IsPatternExpressionLocalRewriter : PatternLocalRewriter
{
/// <summary>
/// Accumulates side-effects that come before the next conjunct.
......@@ -35,10 +35,12 @@ private class IsPatternExpressionLocalRewriter : PatternLocalRewriter
public IsPatternExpressionLocalRewriter(SyntaxNode node, LocalRewriter localRewriter)
: base(node, localRewriter)
{
this._conjunctBuilder = ArrayBuilder<BoundExpression>.GetInstance();
this._sideEffectBuilder = ArrayBuilder<BoundExpression>.GetInstance();
_conjunctBuilder = ArrayBuilder<BoundExpression>.GetInstance();
_sideEffectBuilder = ArrayBuilder<BoundExpression>.GetInstance();
}
protected override bool IsSwitchStatement => false;
public new void Free()
{
_conjunctBuilder.Free();
......
......@@ -16,24 +16,26 @@ internal partial class LocalRewriter
{
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
{
return SwitchLocalRewriter.Rewrite(this, node);
return SwitchStatementLocalRewriter.Rewrite(this, node);
}
private class SwitchLocalRewriter : BaseSwitchLocalRewriter
private sealed class SwitchStatementLocalRewriter : BaseSwitchLocalRewriter
{
/// <summary>
/// A map from section syntax to the first label in that section.
/// </summary>
private readonly Dictionary<SyntaxNode, LabelSymbol> _sectionLabels = PooledDictionary<SyntaxNode, LabelSymbol>.GetInstance();
protected override bool IsSwitchStatement => true;
public static BoundStatement Rewrite(LocalRewriter localRewriter, BoundSwitchStatement node)
{
var rewriter = new SwitchLocalRewriter(node, localRewriter);
var rewriter = new SwitchStatementLocalRewriter(node, localRewriter);
BoundStatement result = rewriter.LowerSwitchStatement(node);
rewriter.Free();
return result;
}
/// <summary>
/// A map from section syntax to the first label in that section.
/// </summary>
private Dictionary<SyntaxNode, LabelSymbol> _sectionLabels = PooledDictionary<SyntaxNode, LabelSymbol>.GetInstance();
/// <summary>
/// We revise the returned label for a leaf so that all leaves in the same switch section are given the same label.
/// This enables the switch emitter to produce better code.
......@@ -62,8 +64,8 @@ protected override LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
return result;
}
private SwitchLocalRewriter(BoundSwitchStatement node, LocalRewriter localRewriter)
: base(node.Syntax, localRewriter, node.SwitchSections.SelectAsArray(section => section.Syntax), isSwitchStatement: true)
private SwitchStatementLocalRewriter(BoundSwitchStatement node, LocalRewriter localRewriter)
: base(node.Syntax, localRewriter, node.SwitchSections.SelectAsArray(section => section.Syntax))
{
}
......
......@@ -16,7 +16,7 @@ internal sealed partial class LocalRewriter
/// <summary>
/// A common base class for lowering constructs that use pattern-matching.
/// </summary>
private class PatternLocalRewriter
private abstract class PatternLocalRewriter
{
protected readonly LocalRewriter _localRewriter;
protected readonly SyntheticBoundNodeFactory _factory;
......@@ -24,27 +24,39 @@ private class PatternLocalRewriter
public PatternLocalRewriter(SyntaxNode node, LocalRewriter localRewriter)
{
this._localRewriter = localRewriter;
this._factory = localRewriter._factory;
this._tempAllocator = new DagTempAllocator(_factory, node);
_localRewriter = localRewriter;
_factory = localRewriter._factory;
_tempAllocator = new DagTempAllocator(_factory, node, IsSwitchStatement);
}
/// <summary>
/// True if this is a rewriter for a switch statement. This affects
/// - sequence points
/// When clause gets a sequence point in a switch statement, but not in a switch expression.
/// - synthesized local variable kind
/// The temp variables must be long lived in a switch statement since their lifetime spans across sequence points.
/// </summary>
protected abstract bool IsSwitchStatement { get; }
public void Free()
{
_tempAllocator.Free();
}
public class DagTempAllocator
public sealed class DagTempAllocator
{
private readonly SyntheticBoundNodeFactory _factory;
private readonly PooledDictionary<BoundDagTemp, BoundExpression> _map = PooledDictionary<BoundDagTemp, BoundExpression>.GetInstance();
private readonly ArrayBuilder<LocalSymbol> _temps = ArrayBuilder<LocalSymbol>.GetInstance();
private readonly SyntaxNode _node;
public DagTempAllocator(SyntheticBoundNodeFactory factory, SyntaxNode node)
private readonly bool _isSwitchStatement;
public DagTempAllocator(SyntheticBoundNodeFactory factory, SyntaxNode node, bool isSwitchStatement)
{
this._factory = factory;
this._node = node;
_factory = factory;
_node = node;
_isSwitchStatement = isSwitchStatement;
}
public void Free()
......@@ -76,7 +88,8 @@ public BoundExpression GetTemp(BoundDagTemp dagTemp)
{
if (!_map.TryGetValue(dagTemp, out BoundExpression result))
{
LocalSymbol temp = _factory.SynthesizedLocal(dagTemp.Type, syntax: _node, kind: SynthesizedLocalKind.SwitchCasePatternMatching);
var kind = _isSwitchStatement ? SynthesizedLocalKind.SwitchCasePatternMatching : SynthesizedLocalKind.LoweringTemp;
LocalSymbol temp = _factory.SynthesizedLocal(dagTemp.Type, syntax: _node, kind: kind);
result = _factory.Local(temp);
_map.Add(dagTemp, result);
_temps.Add(temp);
......
......@@ -20,13 +20,15 @@ public override BoundNode VisitConvertedSwitchExpression(BoundConvertedSwitchExp
return SwitchExpressionLocalRewriter.Rewrite(this, node);
}
private class SwitchExpressionLocalRewriter : BaseSwitchLocalRewriter
private sealed class SwitchExpressionLocalRewriter : BaseSwitchLocalRewriter
{
private SwitchExpressionLocalRewriter(BoundConvertedSwitchExpression node, LocalRewriter localRewriter)
: base(node.Syntax, localRewriter, node.SwitchArms.SelectAsArray(arm => arm.Syntax), isSwitchStatement: false)
: base(node.Syntax, localRewriter, node.SwitchArms.SelectAsArray(arm => arm.Syntax))
{
}
protected override bool IsSwitchStatement => false;
public static BoundExpression Rewrite(LocalRewriter localRewriter, BoundConvertedSwitchExpression node)
{
var rewriter = new SwitchExpressionLocalRewriter(node, localRewriter);
......@@ -55,7 +57,7 @@ private BoundExpression LowerSwitchExpression(BoundConvertedSwitchExpression nod
// decision tree, so the code in result is unreachable at this point.
// Lower each switch expression arm
LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: SynthesizedLocalKind.SwitchCasePatternMatching);
LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: SynthesizedLocalKind.LoweringTemp);
LabelSymbol afterSwitchExpression = _factory.GenerateLabel("afterSwitchExpression");
foreach (BoundSwitchExpressionArm arm in node.SwitchArms)
{
......
......@@ -543,7 +543,6 @@ public override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
public override BoundNode VisitSpillSequence(BoundSpillSequence node)
{
Debug.Assert(node.Locals.All(l => l.SynthesizedKind.IsLongLived()));
var builder = new BoundSpillSequenceBuilder();
// Ensure later errors (e.g. in async rewriting) are associated with the correct node.
......
......@@ -471,7 +471,7 @@ private BoundExpression HoistRefInitialization(SynthesizedLocal local, BoundAssi
if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug)
{
awaitSyntaxOpt = (AwaitExpressionSyntax)local.GetDeclaratorSyntax();
syntaxOffset = this.OriginalMethod.CalculateLocalSyntaxOffset(awaitSyntaxOpt.SpanStart, awaitSyntaxOpt.SyntaxTree);
syntaxOffset = OriginalMethod.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(awaitSyntaxOpt), awaitSyntaxOpt.SyntaxTree);
}
else
{
......
......@@ -175,7 +175,7 @@ protected BoundStatement Rewrite()
// EnC: When emitting the baseline (gen 0) the id is stored in a custom debug information attached to the kickoff method.
// When emitting a delta the id is only used to map to the existing field in the previous generation.
SyntaxNode declaratorSyntax = local.GetDeclaratorSyntax();
int syntaxOffset = this.method.CalculateLocalSyntaxOffset(declaratorSyntax.SpanStart, declaratorSyntax.SyntaxTree);
int syntaxOffset = method.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(declaratorSyntax), declaratorSyntax.SyntaxTree);
int ordinal = synthesizedLocalOrdinals.AssignLocalOrdinal(synthesizedKind, syntaxOffset);
id = new LocalDebugId(syntaxOffset, ordinal);
......
......@@ -460,6 +460,16 @@ internal static bool IsClosureScope(SyntaxNode node)
return false;
}
/// <summary>
/// Given a node that represents a variable declaration, lambda or a closure scope return the position to be used to calculate
/// the node's syntax offset with respect to its containing member.
/// </summary>
internal static int GetDeclaratorPosition(SyntaxNode node)
{
// To differentiate between nested switch expressions that start at the same offset, use the offset of the `switch` keyword.
return (node is SwitchExpressionSyntax switchExpression) ? switchExpression.SwitchKeyword.SpanStart : node.SpanStart;
}
private static SyntaxNode GetLocalFunctionBody(LocalFunctionStatementSyntax localFunctionStatementSyntax)
{
return (SyntaxNode)localFunctionStatementSyntax.Body ?? localFunctionStatementSyntax.ExpressionBody?.Expression;
......
......@@ -303,30 +303,25 @@ .maxstack 1
}");
// Debug
compilation = CompileAndVerify(source, expectedOutput: "True Branch taken", options: TestOptions.DebugExe);
compilation.VerifyIL("C.Main", @"{
// Code size 31 (0x1f)
compilation.VerifyIL("C.Main", @"
{
// Code size 27 (0x1b)
.maxstack 1
.locals init (object V_0,
bool V_1,
object V_2)
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldc.i4.1
IL_0004: call ""void System.Console.Write(bool)""
IL_0009: nop
IL_000a: ldnull
IL_000b: stloc.2
IL_000c: ldc.i4.1
IL_000d: stloc.1
IL_000e: ldloc.1
IL_000f: brfalse.s IL_001e
IL_0011: nop
IL_0012: ldstr "" Branch taken""
IL_0017: call ""void System.Console.Write(string)""
IL_001c: nop
IL_001d: nop
IL_001e: ret
IL_0001: ldc.i4.1
IL_0002: call ""void System.Console.Write(bool)""
IL_0007: nop
IL_0008: ldc.i4.1
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: brfalse.s IL_001a
IL_000d: nop
IL_000e: ldstr "" Branch taken""
IL_0013: call ""void System.Console.Write(string)""
IL_0018: nop
IL_0019: nop
IL_001a: ret
}");
}
......@@ -362,29 +357,24 @@ .maxstack 1
// Debug
compilation = CompileAndVerify(source, expectedOutput: "True Branch taken", options: TestOptions.DebugExe);
compilation.VerifyIL("C.Main", @"{
// Code size 31 (0x1f)
// Code size 27 (0x1b)
.maxstack 1
.locals init (string V_0,
bool V_1,
string V_2)
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldc.i4.1
IL_0004: call ""void System.Console.Write(bool)""
IL_0009: nop
IL_000a: ldnull
IL_000b: stloc.2
IL_000c: ldc.i4.1
IL_000d: stloc.1
IL_000e: ldloc.1
IL_000f: brfalse.s IL_001e
IL_0011: nop
IL_0012: ldstr "" Branch taken""
IL_0017: call ""void System.Console.Write(string)""
IL_001c: nop
IL_001d: nop
IL_001e: ret
IL_0001: ldc.i4.1
IL_0002: call ""void System.Console.Write(bool)""
IL_0007: nop
IL_0008: ldc.i4.1
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: brfalse.s IL_001a
IL_000d: nop
IL_000e: ldstr "" Branch taken""
IL_0013: call ""void System.Console.Write(string)""
IL_0018: nop
IL_0019: nop
IL_001a: ret
}");
}
......
......@@ -570,34 +570,31 @@ public static decimal M(decimal d)
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
compVerifier.VerifyIL("C.M1",
@"{
// Code size 39 (0x27)
// Code size 37 (0x25)
.maxstack 6
.locals init (bool V_0,
decimal V_1,
int V_2)
int V_1)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call ""decimal C.M(decimal)""
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldc.i4.s 10
IL_0007: ldc.i4.s 10
IL_0009: ldc.i4.0
IL_000a: ldc.i4.0
IL_000b: ldc.i4.0
IL_000c: ldc.i4.0
IL_000d: ldc.i4.0
IL_000e: ldc.i4.1
IL_000f: newobj ""decimal..ctor(int, int, int, bool, byte)""
IL_0014: call ""bool decimal.op_Equality(decimal, decimal)""
IL_0019: stloc.0
IL_001a: ldloc.0
IL_001b: brfalse.s IL_0021
IL_001d: ldc.i4.1
IL_001e: stloc.2
IL_001f: br.s IL_0025
IL_0021: ldc.i4.0
IL_0022: stloc.2
IL_0023: br.s IL_0025
IL_0025: ldloc.2
IL_0026: ret
IL_000c: ldc.i4.1
IL_000d: newobj ""decimal..ctor(int, int, int, bool, byte)""
IL_0012: call ""bool decimal.op_Equality(decimal, decimal)""
IL_0017: stloc.0
IL_0018: ldloc.0
IL_0019: brfalse.s IL_001f
IL_001b: ldc.i4.1
IL_001c: stloc.1
IL_001d: br.s IL_0023
IL_001f: ldc.i4.0
IL_0020: stloc.1
IL_0021: br.s IL_0023
IL_0023: ldloc.1
IL_0024: ret
}");
compVerifier.VerifyIL("C.M2",
@"{
......
......@@ -8281,27 +8281,24 @@ .maxstack 1
expectedOutput: "RemoveEmptyEntries");
compVerifier.VerifyIL("Program.Main",
@"{
// Code size 25 (0x19)
// Code size 23 (0x17)
.maxstack 1
.locals init (object V_0, //o
bool V_1,
System.StringSplitOptions V_2)
bool V_1)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.2
IL_0003: ldloc.2
IL_0004: box ""System.StringSplitOptions""
IL_0009: stloc.0
IL_000a: ldc.i4.1
IL_000b: stloc.1
IL_000c: ldloc.1
IL_000d: brfalse.s IL_0018
IL_000f: nop
IL_0010: ldloc.0
IL_0011: call ""void System.Console.WriteLine(object)""
IL_0016: nop
IL_0017: nop
IL_0018: ret
IL_0002: box ""System.StringSplitOptions""
IL_0007: stloc.0
IL_0008: ldc.i4.1
IL_0009: stloc.1
IL_000a: ldloc.1
IL_000b: brfalse.s IL_0016
IL_000d: nop
IL_000e: ldloc.0
IL_000f: call ""void System.Console.WriteLine(object)""
IL_0014: nop
IL_0015: nop
IL_0016: ret
}"
);
}
......
......@@ -3801,42 +3801,38 @@ class C
diff1.VerifyIL("C.F", @"
{
// Code size 52 (0x34)
// Code size 47 (0x2f)
.maxstack 2
.locals init (bool V_0,
[int] V_1,
[int] V_2,
int V_3,
int V_4)
int V_2)
-IL_0000: nop
-IL_0001: ldarg.0
IL_0002: isinst ""bool""
IL_0007: brtrue.s IL_0021
IL_0007: brtrue.s IL_001f
IL_0009: ldarg.0
IL_000a: isinst ""int""
IL_000f: brfalse.s IL_001e
IL_000f: brfalse.s IL_001c
IL_0011: ldarg.0
IL_0012: unbox.any ""int""
IL_0017: stloc.3
IL_0018: ldloc.3
IL_0019: ldc.i4.0
IL_001a: ceq
IL_001c: br.s IL_001f
IL_001e: ldc.i4.0
IL_001f: br.s IL_0022
IL_0021: ldc.i4.1
IL_0022: stloc.0
~IL_0023: ldloc.0
IL_0024: brfalse.s IL_002c
-IL_0026: nop
-IL_0027: ldc.i4.0
IL_0028: stloc.s V_4
IL_002a: br.s IL_0031
-IL_002c: ldc.i4.1
IL_002d: stloc.s V_4
IL_002f: br.s IL_0031
-IL_0031: ldloc.s V_4
IL_0033: ret
IL_0017: ldc.i4.0
IL_0018: ceq
IL_001a: br.s IL_001d
IL_001c: ldc.i4.0
IL_001d: br.s IL_0020
IL_001f: ldc.i4.1
IL_0020: stloc.0
~IL_0021: ldloc.0
IL_0022: brfalse.s IL_0029
-IL_0024: nop
-IL_0025: ldc.i4.0
IL_0026: stloc.2
IL_0027: br.s IL_002d
-IL_0029: ldc.i4.1
IL_002a: stloc.2
IL_002b: br.s IL_002d
-IL_002d: ldloc.2
IL_002e: ret
}", methodToken: diff1.UpdatedMethods.Single());
}
......
......@@ -7288,31 +7288,1620 @@ static void M()
#region Patterns
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void SyntaxOffset_Pattern()
public void SyntaxOffset_IsPattern()
{
var source = @"class C { bool F(object o) => o is int i && o is 3 && o is bool; }";
var c = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll);
c.VerifyPdb("C.F", @"<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""C"" name=""F"" parameterNames=""o"">
<method containingType=""C"" name=""F"" parameterNames=""o"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<encLocalSlotMap>
<slot kind=""0"" offset=""12"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""1"" startColumn=""31"" endLine=""1"" endColumn=""64"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x2d"">
<local name=""i"" il_index=""0"" il_start=""0x0"" il_end=""0x2d"" attributes=""0"" />
</scope>
</method>
</methods>
</symbols>");
}
[WorkItem(37172, "https://github.com/dotnet/roslyn/issues/37172")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void Patterns_SwitchStatement()
{
string source = @"
class C
{
public void Deconstruct() { }
public void Deconstruct(out int x) { x = 1; }
public void Deconstruct(out int x, out object y) { x = 2; y = new C(); }
}
class D
{
public int P { get; set; }
public D Q { get; set; }
public C R { get; set; }
}
class Program
{
static object F() => new C();
static bool B() => true;
static int G(int x) => x;
static int Main()
{
switch (F())
{
// declaration pattern
case int x when G(x) > 10: return 1;
// discard pattern
case bool _: return 2;
// var pattern
case var (y, z): return 3;
// constant pattern
case 4.0: return 4;
// positional patterns
case C() when B(): return 5;
case (): return 6;
case C(int p, C(int q)): return 7;
case C(x: int p): return 8;
// property pattern
case D { P: 1, Q: D { P: 2 }, R: C(int z) }: return 9;
default: return 10;
};
}
}
";
var c = CreateCompilation(source, options: TestOptions.DebugDll, targetFramework: TargetFramework.NetCoreApp30);
var verifier = CompileAndVerify(c, verify: Verification.Skipped);
verifier.VerifyIL("Program.Main", sequencePoints: "Program.Main", expectedIL: @"
{
// Code size 453 (0x1c5)
.maxstack 3
.locals init (int V_0, //x
object V_1, //y
object V_2, //z
int V_3, //p
int V_4, //q
int V_5, //p
int V_6, //z
object V_7,
System.Runtime.CompilerServices.ITuple V_8,
int V_9,
D V_10,
double V_11,
C V_12,
object V_13,
C V_14,
int V_15,
D V_16,
int V_17,
C V_18,
object V_19,
int V_20)
// sequence point: {
IL_0000: nop
// sequence point: switch (F())
IL_0001: call ""object Program.F()""
IL_0006: stloc.s V_19
// sequence point: <hidden>
IL_0008: ldloc.s V_19
IL_000a: stloc.s V_7
// sequence point: <hidden>
IL_000c: ldloc.s V_7
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0022
IL_0015: ldloc.s V_7
IL_0017: unbox.any ""int""
IL_001c: stloc.0
// sequence point: <hidden>
IL_001d: br IL_0162
IL_0022: ldloc.s V_7
IL_0024: isinst ""bool""
IL_0029: brtrue IL_0173
IL_002e: ldloc.s V_7
IL_0030: isinst ""System.Runtime.CompilerServices.ITuple""
IL_0035: stloc.s V_8
IL_0037: ldloc.s V_8
IL_0039: brfalse.s IL_0093
IL_003b: ldloc.s V_8
IL_003d: callvirt ""int System.Runtime.CompilerServices.ITuple.Length.get""
IL_0042: stloc.s V_9
// sequence point: <hidden>
IL_0044: ldloc.s V_9
IL_0046: ldc.i4.2
IL_0047: bne.un.s IL_0060
IL_0049: ldloc.s V_8
IL_004b: ldc.i4.0
IL_004c: callvirt ""object System.Runtime.CompilerServices.ITuple.this[int].get""
IL_0051: stloc.1
// sequence point: <hidden>
IL_0052: ldloc.s V_8
IL_0054: ldc.i4.1
IL_0055: callvirt ""object System.Runtime.CompilerServices.ITuple.this[int].get""
IL_005a: stloc.2
// sequence point: <hidden>
IL_005b: br IL_0178
IL_0060: ldloc.s V_7
IL_0062: isinst ""C""
IL_0067: brtrue IL_0184
IL_006c: br.s IL_0077
IL_006e: ldloc.s V_9
IL_0070: brfalse IL_01a1
IL_0075: br.s IL_00c8
IL_0077: ldloc.s V_9
IL_0079: brfalse IL_01a1
IL_007e: ldloc.s V_7
IL_0080: isinst ""D""
IL_0085: stloc.s V_10
IL_0087: ldloc.s V_10
IL_0089: brtrue IL_011a
IL_008e: br IL_01bc
IL_0093: ldloc.s V_7
IL_0095: isinst ""double""
IL_009a: brfalse.s IL_00ba
IL_009c: ldloc.s V_7
IL_009e: unbox.any ""double""
IL_00a3: stloc.s V_11
// sequence point: <hidden>
IL_00a5: ldloc.s V_11
IL_00a7: ldc.r8 4
IL_00b0: beq IL_017f
IL_00b5: br IL_01bc
IL_00ba: ldloc.s V_7
IL_00bc: isinst ""C""
IL_00c1: brtrue IL_0190
IL_00c6: br.s IL_0108
IL_00c8: ldloc.s V_7
IL_00ca: castclass ""C""
IL_00cf: stloc.s V_12
// sequence point: <hidden>
IL_00d1: ldloc.s V_12
IL_00d3: ldloca.s V_3
IL_00d5: ldloca.s V_13
IL_00d7: callvirt ""void C.Deconstruct(out int, out object)""
IL_00dc: nop
// sequence point: <hidden>
IL_00dd: ldloc.s V_13
IL_00df: isinst ""C""
IL_00e4: stloc.s V_14
IL_00e6: ldloc.s V_14
IL_00e8: brfalse.s IL_00f9
IL_00ea: ldloc.s V_14
IL_00ec: ldloca.s V_4
IL_00ee: callvirt ""void C.Deconstruct(out int)""
IL_00f3: nop
// sequence point: <hidden>
IL_00f4: br IL_01a6
IL_00f9: ldloc.s V_12
IL_00fb: ldloca.s V_5
IL_00fd: callvirt ""void C.Deconstruct(out int)""
IL_0102: nop
// sequence point: <hidden>
IL_0103: br IL_01ad
IL_0108: ldloc.s V_7
IL_010a: isinst ""D""
IL_010f: stloc.s V_10
IL_0111: ldloc.s V_10
IL_0113: brtrue.s IL_011a
IL_0115: br IL_01bc
// sequence point: <hidden>
IL_011a: ldloc.s V_10
IL_011c: callvirt ""int D.P.get""
IL_0121: stloc.s V_15
// sequence point: <hidden>
IL_0123: ldloc.s V_15
IL_0125: ldc.i4.1
IL_0126: bne.un IL_01bc
IL_012b: ldloc.s V_10
IL_012d: callvirt ""D D.Q.get""
IL_0132: stloc.s V_16
// sequence point: <hidden>
IL_0134: ldloc.s V_16
IL_0136: brfalse IL_01bc
IL_013b: ldloc.s V_16
IL_013d: callvirt ""int D.P.get""
IL_0142: stloc.s V_17
// sequence point: <hidden>
IL_0144: ldloc.s V_17
IL_0146: ldc.i4.2
IL_0147: bne.un.s IL_01bc
IL_0149: ldloc.s V_10
IL_014b: callvirt ""C D.R.get""
IL_0150: stloc.s V_18
// sequence point: <hidden>
IL_0152: ldloc.s V_18
IL_0154: brfalse.s IL_01bc
IL_0156: ldloc.s V_18
IL_0158: ldloca.s V_6
IL_015a: callvirt ""void C.Deconstruct(out int)""
IL_015f: nop
// sequence point: <hidden>
IL_0160: br.s IL_01b4
// sequence point: when G(x) > 10
IL_0162: ldloc.0
IL_0163: call ""int Program.G(int)""
IL_0168: ldc.i4.s 10
IL_016a: bgt.s IL_016e
// sequence point: <hidden>
IL_016c: br.s IL_01bc
// sequence point: return 1;
IL_016e: ldc.i4.1
IL_016f: stloc.s V_20
IL_0171: br.s IL_01c2
// sequence point: return 2;
IL_0173: ldc.i4.2
IL_0174: stloc.s V_20
IL_0176: br.s IL_01c2
// sequence point: <hidden>
IL_0178: br.s IL_017a
// sequence point: return 3;
IL_017a: ldc.i4.3
IL_017b: stloc.s V_20
IL_017d: br.s IL_01c2
// sequence point: return 4;
IL_017f: ldc.i4.4
IL_0180: stloc.s V_20
IL_0182: br.s IL_01c2
// sequence point: when B()
IL_0184: call ""bool Program.B()""
IL_0189: brtrue.s IL_019c
// sequence point: <hidden>
IL_018b: br IL_006e
// sequence point: when B()
IL_0190: call ""bool Program.B()""
IL_0195: brtrue.s IL_019c
// sequence point: <hidden>
IL_0197: br IL_00c8
// sequence point: return 5;
IL_019c: ldc.i4.5
IL_019d: stloc.s V_20
IL_019f: br.s IL_01c2
// sequence point: return 6;
IL_01a1: ldc.i4.6
IL_01a2: stloc.s V_20
IL_01a4: br.s IL_01c2
// sequence point: <hidden>
IL_01a6: br.s IL_01a8
// sequence point: return 7;
IL_01a8: ldc.i4.7
IL_01a9: stloc.s V_20
IL_01ab: br.s IL_01c2
// sequence point: <hidden>
IL_01ad: br.s IL_01af
// sequence point: return 8;
IL_01af: ldc.i4.8
IL_01b0: stloc.s V_20
IL_01b2: br.s IL_01c2
// sequence point: <hidden>
IL_01b4: br.s IL_01b6
// sequence point: return 9;
IL_01b6: ldc.i4.s 9
IL_01b8: stloc.s V_20
IL_01ba: br.s IL_01c2
// sequence point: return 10;
IL_01bc: ldc.i4.s 10
IL_01be: stloc.s V_20
IL_01c0: br.s IL_01c2
// sequence point: }
IL_01c2: ldloc.s V_20
IL_01c4: ret
}
", source: source);
verifier.VerifyPdb("Program.Main", @"
<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""Program"" name=""Main"">
<customDebugInfo>
<forward declaringType=""C"" methodName=""Deconstruct"" />
<encLocalSlotMap>
<slot kind=""0"" offset=""93"" />
<slot kind=""0"" offset=""244"" />
<slot kind=""0"" offset=""247"" />
<slot kind=""0"" offset=""465"" />
<slot kind=""0"" offset=""474"" />
<slot kind=""0"" offset=""516"" />
<slot kind=""0"" offset=""617"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""35"" offset=""11"" ordinal=""1"" />
<slot kind=""35"" offset=""11"" ordinal=""2"" />
<slot kind=""35"" offset=""11"" ordinal=""3"" />
<slot kind=""35"" offset=""11"" ordinal=""4"" />
<slot kind=""35"" offset=""11"" ordinal=""5"" />
<slot kind=""35"" offset=""11"" ordinal=""6"" />
<slot kind=""35"" offset=""11"" ordinal=""7"" />
<slot kind=""35"" offset=""11"" ordinal=""8"" />
<slot kind=""35"" offset=""11"" ordinal=""9"" />
<slot kind=""35"" offset=""11"" ordinal=""10"" />
<slot kind=""35"" offset=""11"" ordinal=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""23"" startColumn=""5"" endLine=""23"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""24"" startColumn=""9"" endLine=""24"" endColumn=""21"" document=""1"" />
<entry offset=""0x8"" hidden=""true"" document=""1"" />
<entry offset=""0xc"" hidden=""true"" document=""1"" />
<entry offset=""0x1d"" hidden=""true"" document=""1"" />
<entry offset=""0x44"" hidden=""true"" document=""1"" />
<entry offset=""0x52"" hidden=""true"" document=""1"" />
<entry offset=""0x5b"" hidden=""true"" document=""1"" />
<entry offset=""0xa5"" hidden=""true"" document=""1"" />
<entry offset=""0xd1"" hidden=""true"" document=""1"" />
<entry offset=""0xdd"" hidden=""true"" document=""1"" />
<entry offset=""0xf4"" hidden=""true"" document=""1"" />
<entry offset=""0x103"" hidden=""true"" document=""1"" />
<entry offset=""0x11a"" hidden=""true"" document=""1"" />
<entry offset=""0x123"" hidden=""true"" document=""1"" />
<entry offset=""0x134"" hidden=""true"" document=""1"" />
<entry offset=""0x144"" hidden=""true"" document=""1"" />
<entry offset=""0x152"" hidden=""true"" document=""1"" />
<entry offset=""0x160"" hidden=""true"" document=""1"" />
<entry offset=""0x162"" startLine=""27"" startColumn=""24"" endLine=""27"" endColumn=""38"" document=""1"" />
<entry offset=""0x16c"" hidden=""true"" document=""1"" />
<entry offset=""0x16e"" startLine=""27"" startColumn=""40"" endLine=""27"" endColumn=""49"" document=""1"" />
<entry offset=""0x173"" startLine=""30"" startColumn=""26"" endLine=""30"" endColumn=""35"" document=""1"" />
<entry offset=""0x178"" hidden=""true"" document=""1"" />
<entry offset=""0x17a"" startLine=""33"" startColumn=""30"" endLine=""33"" endColumn=""39"" document=""1"" />
<entry offset=""0x17f"" startLine=""36"" startColumn=""23"" endLine=""36"" endColumn=""32"" document=""1"" />
<entry offset=""0x184"" startLine=""39"" startColumn=""22"" endLine=""39"" endColumn=""30"" document=""1"" />
<entry offset=""0x18b"" hidden=""true"" document=""1"" />
<entry offset=""0x190"" startLine=""39"" startColumn=""22"" endLine=""39"" endColumn=""30"" document=""1"" />
<entry offset=""0x197"" hidden=""true"" document=""1"" />
<entry offset=""0x19c"" startLine=""39"" startColumn=""32"" endLine=""39"" endColumn=""41"" document=""1"" />
<entry offset=""0x1a1"" startLine=""40"" startColumn=""22"" endLine=""40"" endColumn=""31"" document=""1"" />
<entry offset=""0x1a6"" hidden=""true"" document=""1"" />
<entry offset=""0x1a8"" startLine=""41"" startColumn=""38"" endLine=""41"" endColumn=""47"" document=""1"" />
<entry offset=""0x1ad"" hidden=""true"" document=""1"" />
<entry offset=""0x1af"" startLine=""42"" startColumn=""31"" endLine=""42"" endColumn=""40"" document=""1"" />
<entry offset=""0x1b4"" hidden=""true"" document=""1"" />
<entry offset=""0x1b6"" startLine=""45"" startColumn=""58"" endLine=""45"" endColumn=""67"" document=""1"" />
<entry offset=""0x1bc"" startLine=""47"" startColumn=""22"" endLine=""47"" endColumn=""32"" document=""1"" />
<entry offset=""0x1c2"" startLine=""49"" startColumn=""5"" endLine=""49"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x1c5"">
<scope startOffset=""0x162"" endOffset=""0x173"">
<local name=""x"" il_index=""0"" il_start=""0x162"" il_end=""0x173"" attributes=""0"" />
</scope>
<scope startOffset=""0x178"" endOffset=""0x17f"">
<local name=""y"" il_index=""1"" il_start=""0x178"" il_end=""0x17f"" attributes=""0"" />
<local name=""z"" il_index=""2"" il_start=""0x178"" il_end=""0x17f"" attributes=""0"" />
</scope>
<scope startOffset=""0x1a6"" endOffset=""0x1ad"">
<local name=""p"" il_index=""3"" il_start=""0x1a6"" il_end=""0x1ad"" attributes=""0"" />
<local name=""q"" il_index=""4"" il_start=""0x1a6"" il_end=""0x1ad"" attributes=""0"" />
</scope>
<scope startOffset=""0x1ad"" endOffset=""0x1b4"">
<local name=""p"" il_index=""5"" il_start=""0x1ad"" il_end=""0x1b4"" attributes=""0"" />
</scope>
<scope startOffset=""0x1b4"" endOffset=""0x1bc"">
<local name=""z"" il_index=""6"" il_start=""0x1b4"" il_end=""0x1bc"" attributes=""0"" />
</scope>
</scope>
</method>
</methods>
</symbols>
");
}
[WorkItem(37172, "https://github.com/dotnet/roslyn/issues/37172")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void Patterns_SwitchExpression()
{
string source = @"
class C
{
public void Deconstruct() { }
public void Deconstruct(out int x) { x = 1; }
public void Deconstruct(out int x, out object y) { x = 2; y = new C(); }
}
class D
{
public int P { get; set; }
public D Q { get; set; }
public C R { get; set; }
}
class Program
{
static object F() => new C();
static bool B() => true;
static int G(int x) => x;
static void Main()
{
var a = F() switch
{
// declaration pattern
int x when G(x) > 10 => 1,
// discard pattern
bool _ => 2,
// var pattern
var (y, z) => 3,
// constant pattern
4.0 => 4,
// positional patterns
C() when B() => 5,
() => 6,
C(int p, C(int q)) => 7,
C(x: int p) => 8,
// property pattern
D { P: 1, Q: D { P: 2 }, R: C (int z) } => 9,
_ => 10,
};
}
}
";
var c = CreateCompilation(source, options: TestOptions.DebugDll, targetFramework: TargetFramework.NetCoreApp30);
var verifier = CompileAndVerify(c, verify: Verification.Skipped);
// note no sequence points emitted within the switch expression
verifier.VerifyIL("Program.Main", sequencePoints: "Program.Main", expectedIL: @"
{
// Code size 454 (0x1c6)
.maxstack 3
.locals init (int V_0, //a
int V_1, //x
object V_2, //y
object V_3, //z
int V_4, //p
int V_5, //q
int V_6, //p
int V_7, //z
int V_8,
object V_9,
System.Runtime.CompilerServices.ITuple V_10,
int V_11,
D V_12,
double V_13,
C V_14,
object V_15,
C V_16,
int V_17,
D V_18,
int V_19,
C V_20,
int V_21)
-IL_0000: nop
-IL_0001: call ""object Program.F()""
IL_0006: stloc.s V_9
IL_0008: ldloc.s V_9
IL_000a: isinst ""int""
IL_000f: brfalse.s IL_001e
IL_0011: ldloc.s V_9
IL_0013: unbox.any ""int""
IL_0018: stloc.1
IL_0019: br IL_015e
IL_001e: ldloc.s V_9
IL_0020: isinst ""bool""
IL_0025: brtrue IL_016f
IL_002a: ldloc.s V_9
IL_002c: isinst ""System.Runtime.CompilerServices.ITuple""
IL_0031: stloc.s V_10
IL_0033: ldloc.s V_10
IL_0035: brfalse.s IL_008f
IL_0037: ldloc.s V_10
IL_0039: callvirt ""int System.Runtime.CompilerServices.ITuple.Length.get""
IL_003e: stloc.s V_11
IL_0040: ldloc.s V_11
IL_0042: ldc.i4.2
IL_0043: bne.un.s IL_005c
IL_0045: ldloc.s V_10
IL_0047: ldc.i4.0
IL_0048: callvirt ""object System.Runtime.CompilerServices.ITuple.this[int].get""
IL_004d: stloc.2
IL_004e: ldloc.s V_10
IL_0050: ldc.i4.1
IL_0051: callvirt ""object System.Runtime.CompilerServices.ITuple.this[int].get""
IL_0056: stloc.3
IL_0057: br IL_0174
IL_005c: ldloc.s V_9
IL_005e: isinst ""C""
IL_0063: brtrue IL_0180
IL_0068: br.s IL_0073
IL_006a: ldloc.s V_11
IL_006c: brfalse IL_019d
IL_0071: br.s IL_00c4
IL_0073: ldloc.s V_11
IL_0075: brfalse IL_019d
IL_007a: ldloc.s V_9
IL_007c: isinst ""D""
IL_0081: stloc.s V_12
IL_0083: ldloc.s V_12
IL_0085: brtrue IL_0116
IL_008a: br IL_01b8
IL_008f: ldloc.s V_9
IL_0091: isinst ""double""
IL_0096: brfalse.s IL_00b6
IL_0098: ldloc.s V_9
IL_009a: unbox.any ""double""
IL_009f: stloc.s V_13
IL_00a1: ldloc.s V_13
IL_00a3: ldc.r8 4
IL_00ac: beq IL_017b
IL_00b1: br IL_01b8
IL_00b6: ldloc.s V_9
IL_00b8: isinst ""C""
IL_00bd: brtrue IL_018c
IL_00c2: br.s IL_0104
IL_00c4: ldloc.s V_9
IL_00c6: castclass ""C""
IL_00cb: stloc.s V_14
IL_00cd: ldloc.s V_14
IL_00cf: ldloca.s V_4
IL_00d1: ldloca.s V_15
IL_00d3: callvirt ""void C.Deconstruct(out int, out object)""
IL_00d8: nop
IL_00d9: ldloc.s V_15
IL_00db: isinst ""C""
IL_00e0: stloc.s V_16
IL_00e2: ldloc.s V_16
IL_00e4: brfalse.s IL_00f5
IL_00e6: ldloc.s V_16
IL_00e8: ldloca.s V_5
IL_00ea: callvirt ""void C.Deconstruct(out int)""
IL_00ef: nop
IL_00f0: br IL_01a2
IL_00f5: ldloc.s V_14
IL_00f7: ldloca.s V_6
IL_00f9: callvirt ""void C.Deconstruct(out int)""
IL_00fe: nop
IL_00ff: br IL_01a9
IL_0104: ldloc.s V_9
IL_0106: isinst ""D""
IL_010b: stloc.s V_12
IL_010d: ldloc.s V_12
IL_010f: brtrue.s IL_0116
IL_0111: br IL_01b8
IL_0116: ldloc.s V_12
IL_0118: callvirt ""int D.P.get""
IL_011d: stloc.s V_17
IL_011f: ldloc.s V_17
IL_0121: ldc.i4.1
IL_0122: bne.un IL_01b8
IL_0127: ldloc.s V_12
IL_0129: callvirt ""D D.Q.get""
IL_012e: stloc.s V_18
IL_0130: ldloc.s V_18
IL_0132: brfalse IL_01b8
IL_0137: ldloc.s V_18
IL_0139: callvirt ""int D.P.get""
IL_013e: stloc.s V_19
IL_0140: ldloc.s V_19
IL_0142: ldc.i4.2
IL_0143: bne.un.s IL_01b8
IL_0145: ldloc.s V_12
IL_0147: callvirt ""C D.R.get""
IL_014c: stloc.s V_20
IL_014e: ldloc.s V_20
IL_0150: brfalse.s IL_01b8
IL_0152: ldloc.s V_20
IL_0154: ldloca.s V_7
IL_0156: callvirt ""void C.Deconstruct(out int)""
IL_015b: nop
IL_015c: br.s IL_01b0
IL_015e: ldloc.1
IL_015f: call ""int Program.G(int)""
IL_0164: ldc.i4.s 10
IL_0166: bgt.s IL_016a
IL_0168: br.s IL_01b8
IL_016a: ldc.i4.1
IL_016b: stloc.s V_8
IL_016d: br.s IL_01be
IL_016f: ldc.i4.2
IL_0170: stloc.s V_8
IL_0172: br.s IL_01be
IL_0174: br.s IL_0176
IL_0176: ldc.i4.3
IL_0177: stloc.s V_8
IL_0179: br.s IL_01be
IL_017b: ldc.i4.4
IL_017c: stloc.s V_8
IL_017e: br.s IL_01be
IL_0180: call ""bool Program.B()""
IL_0185: brtrue.s IL_0198
IL_0187: br IL_006a
IL_018c: call ""bool Program.B()""
IL_0191: brtrue.s IL_0198
IL_0193: br IL_00c4
IL_0198: ldc.i4.5
IL_0199: stloc.s V_8
IL_019b: br.s IL_01be
IL_019d: ldc.i4.6
IL_019e: stloc.s V_8
IL_01a0: br.s IL_01be
IL_01a2: br.s IL_01a4
IL_01a4: ldc.i4.7
IL_01a5: stloc.s V_8
IL_01a7: br.s IL_01be
IL_01a9: br.s IL_01ab
IL_01ab: ldc.i4.8
IL_01ac: stloc.s V_8
IL_01ae: br.s IL_01be
IL_01b0: br.s IL_01b2
IL_01b2: ldc.i4.s 9
IL_01b4: stloc.s V_8
IL_01b6: br.s IL_01be
IL_01b8: ldc.i4.s 10
IL_01ba: stloc.s V_8
IL_01bc: br.s IL_01be
IL_01be: ldloc.s V_8
IL_01c0: stloc.s V_21
IL_01c2: ldloc.s V_21
IL_01c4: stloc.0
-IL_01c5: ret
}
");
verifier.VerifyPdb("Program.Main", @"
<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""Program"" name=""Main"">
<customDebugInfo>
<forward declaringType=""C"" methodName=""Deconstruct"" />
<encLocalSlotMap>
<slot kind=""0"" offset=""15"" />
<slot kind=""0"" offset=""94"" />
<slot kind=""0"" offset=""225"" />
<slot kind=""0"" offset=""228"" />
<slot kind=""0"" offset=""406"" />
<slot kind=""0"" offset=""415"" />
<slot kind=""0"" offset=""447"" />
<slot kind=""0"" offset=""539"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""28"" offset=""23"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""23"" startColumn=""5"" endLine=""23"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""24"" startColumn=""9"" endLine=""48"" endColumn=""11"" document=""1"" />
<entry offset=""0x1c5"" startLine=""49"" startColumn=""5"" endLine=""49"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x1c6"">
<local name=""a"" il_index=""0"" il_start=""0x0"" il_end=""0x1c6"" attributes=""0"" />
<scope startOffset=""0x1"" endOffset=""0x1c5"">
<local name=""x"" il_index=""1"" il_start=""0x1"" il_end=""0x1c5"" attributes=""0"" />
<local name=""y"" il_index=""2"" il_start=""0x1"" il_end=""0x1c5"" attributes=""0"" />
<local name=""z"" il_index=""3"" il_start=""0x1"" il_end=""0x1c5"" attributes=""0"" />
<local name=""p"" il_index=""4"" il_start=""0x1"" il_end=""0x1c5"" attributes=""0"" />
<local name=""q"" il_index=""5"" il_start=""0x1"" il_end=""0x1c5"" attributes=""0"" />
<local name=""p"" il_index=""6"" il_start=""0x1"" il_end=""0x1c5"" attributes=""0"" />
<local name=""z"" il_index=""7"" il_start=""0x1"" il_end=""0x1c5"" attributes=""0"" />
<scope startOffset=""0x15e"" endOffset=""0x16f"">
<local name=""x"" il_index=""1"" il_start=""0x15e"" il_end=""0x16f"" attributes=""0"" />
</scope>
<scope startOffset=""0x174"" endOffset=""0x17b"">
<local name=""y"" il_index=""2"" il_start=""0x174"" il_end=""0x17b"" attributes=""0"" />
<local name=""z"" il_index=""3"" il_start=""0x174"" il_end=""0x17b"" attributes=""0"" />
</scope>
<scope startOffset=""0x1a2"" endOffset=""0x1a9"">
<local name=""p"" il_index=""4"" il_start=""0x1a2"" il_end=""0x1a9"" attributes=""0"" />
<local name=""q"" il_index=""5"" il_start=""0x1a2"" il_end=""0x1a9"" attributes=""0"" />
</scope>
<scope startOffset=""0x1a9"" endOffset=""0x1b0"">
<local name=""p"" il_index=""6"" il_start=""0x1a9"" il_end=""0x1b0"" attributes=""0"" />
</scope>
<scope startOffset=""0x1b0"" endOffset=""0x1b8"">
<local name=""z"" il_index=""7"" il_start=""0x1b0"" il_end=""0x1b8"" attributes=""0"" />
</scope>
</scope>
</scope>
</method>
</methods>
</symbols>
");
}
[WorkItem(37172, "https://github.com/dotnet/roslyn/issues/37172")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void Patterns_IsPattern()
{
string source = @"
class C
{
public void Deconstruct() { }
public void Deconstruct(out int x) { x = 1; }
public void Deconstruct(out int x, out object y) { x = 2; y = new C(); }
}
class D
{
public int P { get; set; }
public D Q { get; set; }
public C R { get; set; }
}
class Program
{
static object F() => new C();
static bool B() => true;
static int G(int x) => x;
static bool M()
{
object obj = F();
return
// declaration pattern
obj is int x ||
// discard pattern
obj is bool _ ||
// var pattern
obj is var (y, z1) ||
// constant pattern
obj is 4.0 ||
// positional patterns
obj is C() ||
obj is () ||
obj is C(int p1, C(int q)) ||
obj is C(x: int p2) ||
// property pattern
obj is D { P: 1, Q: D { P: 2 }, R: C(int z2) };
}
}
";
var c = CreateCompilation(source, options: TestOptions.DebugDll, targetFramework: TargetFramework.NetCoreApp30);
var verifier = CompileAndVerify(c, verify: Verification.Skipped);
verifier.VerifyIL("Program.M", sequencePoints: "Program.M", expectedIL: @"
{
// Code size 301 (0x12d)
.maxstack 3
.locals init (object V_0, //obj
int V_1, //x
object V_2, //y
object V_3, //z1
int V_4, //p1
int V_5, //q
int V_6, //p2
int V_7, //z2
System.Runtime.CompilerServices.ITuple V_8,
C V_9,
object V_10,
C V_11,
D V_12,
D V_13,
bool V_14)
-IL_0000: nop
-IL_0001: call ""object Program.F()""
IL_0006: stloc.0
-IL_0007: ldloc.0
IL_0008: isinst ""int""
IL_000d: brfalse.s IL_001b
IL_000f: ldloc.0
IL_0010: unbox.any ""int""
IL_0015: stloc.1
IL_0016: br IL_0125
IL_001b: ldloc.0
IL_001c: isinst ""bool""
IL_0021: brtrue IL_0125
IL_0026: ldloc.0
IL_0027: isinst ""System.Runtime.CompilerServices.ITuple""
IL_002c: stloc.s V_8
IL_002e: ldloc.s V_8
IL_0030: brfalse.s IL_0053
IL_0032: ldloc.s V_8
IL_0034: callvirt ""int System.Runtime.CompilerServices.ITuple.Length.get""
IL_0039: ldc.i4.2
IL_003a: bne.un.s IL_0053
IL_003c: ldloc.s V_8
IL_003e: ldc.i4.0
IL_003f: callvirt ""object System.Runtime.CompilerServices.ITuple.this[int].get""
IL_0044: stloc.2
IL_0045: ldloc.s V_8
IL_0047: ldc.i4.1
IL_0048: callvirt ""object System.Runtime.CompilerServices.ITuple.this[int].get""
IL_004d: stloc.3
IL_004e: br IL_0125
IL_0053: ldloc.0
IL_0054: isinst ""double""
IL_0059: brfalse.s IL_006f
IL_005b: ldloc.0
IL_005c: unbox.any ""double""
IL_0061: ldc.r8 4
IL_006a: beq IL_0125
IL_006f: ldloc.0
IL_0070: isinst ""C""
IL_0075: brtrue IL_0125
IL_007a: ldloc.0
IL_007b: isinst ""System.Runtime.CompilerServices.ITuple""
IL_0080: stloc.s V_8
IL_0082: ldloc.s V_8
IL_0084: brfalse.s IL_0092
IL_0086: ldloc.s V_8
IL_0088: callvirt ""int System.Runtime.CompilerServices.ITuple.Length.get""
IL_008d: brfalse IL_0125
IL_0092: ldloc.0
IL_0093: isinst ""C""
IL_0098: stloc.s V_9
IL_009a: ldloc.s V_9
IL_009c: brfalse.s IL_00c3
IL_009e: ldloc.s V_9
IL_00a0: ldloca.s V_4
IL_00a2: ldloca.s V_10
IL_00a4: callvirt ""void C.Deconstruct(out int, out object)""
IL_00a9: nop
IL_00aa: ldloc.s V_10
IL_00ac: isinst ""C""
IL_00b1: stloc.s V_11
IL_00b3: ldloc.s V_11
IL_00b5: brfalse.s IL_00c3
IL_00b7: ldloc.s V_11
IL_00b9: ldloca.s V_5
IL_00bb: callvirt ""void C.Deconstruct(out int)""
IL_00c0: nop
IL_00c1: br.s IL_0125
IL_00c3: ldloc.0
IL_00c4: isinst ""C""
IL_00c9: stloc.s V_11
IL_00cb: ldloc.s V_11
IL_00cd: brfalse.s IL_00db
IL_00cf: ldloc.s V_11
IL_00d1: ldloca.s V_6
IL_00d3: callvirt ""void C.Deconstruct(out int)""
IL_00d8: nop
IL_00d9: br.s IL_0125
IL_00db: ldloc.0
IL_00dc: isinst ""D""
IL_00e1: stloc.s V_12
IL_00e3: ldloc.s V_12
IL_00e5: brfalse.s IL_0122
IL_00e7: ldloc.s V_12
IL_00e9: callvirt ""int D.P.get""
IL_00ee: ldc.i4.1
IL_00ef: bne.un.s IL_0122
IL_00f1: ldloc.s V_12
IL_00f3: callvirt ""D D.Q.get""
IL_00f8: stloc.s V_13
IL_00fa: ldloc.s V_13
IL_00fc: brfalse.s IL_0122
IL_00fe: ldloc.s V_13
IL_0100: callvirt ""int D.P.get""
IL_0105: ldc.i4.2
IL_0106: bne.un.s IL_0122
IL_0108: ldloc.s V_12
IL_010a: callvirt ""C D.R.get""
IL_010f: stloc.s V_11
IL_0111: ldloc.s V_11
IL_0113: brfalse.s IL_0122
IL_0115: ldloc.s V_11
IL_0117: ldloca.s V_7
IL_0119: callvirt ""void C.Deconstruct(out int)""
IL_011e: nop
IL_011f: ldc.i4.1
IL_0120: br.s IL_0123
IL_0122: ldc.i4.0
IL_0123: br.s IL_0126
IL_0125: ldc.i4.1
IL_0126: stloc.s V_14
IL_0128: br.s IL_012a
-IL_012a: ldloc.s V_14
IL_012c: ret
}
");
verifier.VerifyPdb("Program.M", @"
<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""Program"" name=""M"">
<customDebugInfo>
<forward declaringType=""C"" methodName=""Deconstruct"" />
<encLocalSlotMap>
<slot kind=""0"" offset=""18"" />
<slot kind=""0"" offset=""106"" />
<slot kind=""0"" offset=""230"" />
<slot kind=""0"" offset=""233"" />
<slot kind=""0"" offset=""419"" />
<slot kind=""0"" offset=""429"" />
<slot kind=""0"" offset=""465"" />
<slot kind=""0"" offset=""561"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""23"" startColumn=""5"" endLine=""23"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""24"" startColumn=""9"" endLine=""24"" endColumn=""26"" document=""1"" />
<entry offset=""0x7"" startLine=""25"" startColumn=""9"" endLine=""45"" endColumn=""60"" document=""1"" />
<entry offset=""0x12a"" startLine=""46"" startColumn=""5"" endLine=""46"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x12d"">
<local name=""obj"" il_index=""0"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
<local name=""x"" il_index=""1"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
<local name=""y"" il_index=""2"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
<local name=""z1"" il_index=""3"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
<local name=""p1"" il_index=""4"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
<local name=""q"" il_index=""5"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
<local name=""p2"" il_index=""6"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
<local name=""z2"" il_index=""7"" il_start=""0x0"" il_end=""0x12d"" attributes=""0"" />
</scope>
</method>
</methods>
</symbols>
");
}
[WorkItem(37172, "https://github.com/dotnet/roslyn/issues/37172")]
[WorkItem(37232, "https://github.com/dotnet/roslyn/issues/37232")]
[WorkItem(37237, "https://github.com/dotnet/roslyn/issues/37237")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void Patterns_SwitchExpression_Closures()
{
string source = @"
using System;
public class C
{
static int M()
{
return F() switch
{
1 => F() switch
{
C { P: int p, Q: C { P: int q } } => G(() => p + q),
_ => 10
},
2 => F() switch
{
C { P: int r } => G(() => r),
_ => 20
},
C { Q: int s } => G(() => s),
_ => 0
}
switch
{
var t when t > 0 => G(() => t),
_ => 0
};
}
object P { get; set; }
object Q { get; set; }
static object F() => null;
static int G(Func<int> f) => 0;
}
";
var c = CreateCompilation(source, options: TestOptions.DebugDll);
var verifier = CompileAndVerify(c);
// TODO: https://github.com/dotnet/roslyn/issues/37237
// There should be no sequence points emitted within the switch expressions.
//
// TODO: https://github.com/dotnet/roslyn/issues/37232
// The values of the closure offsets are incorrect.
verifier.VerifyIL("C.M", sequencePoints: "C.M", expectedIL: @"
{
// Code size 459 (0x1cb)
.maxstack 2
.locals init (C.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0
int V_1,
int V_2,
C.<>c__DisplayClass0_1 V_3, //CS$<>8__locals1
int V_4,
object V_5,
int V_6,
C V_7,
object V_8,
int V_9,
C.<>c__DisplayClass0_2 V_10, //CS$<>8__locals2
int V_11,
object V_12,
C V_13,
object V_14,
object V_15,
C V_16,
object V_17,
int V_18,
C.<>c__DisplayClass0_3 V_19, //CS$<>8__locals3
int V_20,
int V_21)
-IL_0000: nop
~IL_0001: newobj ""C.<>c__DisplayClass0_0..ctor()""
IL_0006: stloc.0
~IL_0007: newobj ""C.<>c__DisplayClass0_1..ctor()""
IL_000c: stloc.3
IL_000d: call ""object C.F()""
IL_0012: stloc.s V_5
IL_0014: ldloc.s V_5
IL_0016: isinst ""int""
IL_001b: brfalse.s IL_003a
IL_001d: ldloc.s V_5
IL_001f: unbox.any ""int""
IL_0024: stloc.s V_6
IL_0026: ldloc.s V_6
IL_0028: ldc.i4.1
IL_0029: beq.s IL_0071
IL_002b: br.s IL_002d
IL_002d: ldloc.s V_6
IL_002f: ldc.i4.2
IL_0030: beq IL_010f
IL_0035: br IL_018b
IL_003a: ldloc.s V_5
IL_003c: isinst ""C""
IL_0041: stloc.s V_7
IL_0043: ldloc.s V_7
IL_0045: brfalse IL_018b
IL_004a: ldloc.s V_7
IL_004c: callvirt ""object C.Q.get""
IL_0051: stloc.s V_8
IL_0053: ldloc.s V_8
IL_0055: isinst ""int""
IL_005a: brfalse IL_018b
IL_005f: ldloc.3
IL_0060: ldloc.s V_8
IL_0062: unbox.any ""int""
IL_0067: stfld ""int C.<>c__DisplayClass0_1.<s>5__3""
IL_006c: br IL_0174
~IL_0071: newobj ""C.<>c__DisplayClass0_2..ctor()""
IL_0076: stloc.s V_10
IL_0078: call ""object C.F()""
IL_007d: stloc.s V_12
IL_007f: ldloc.s V_12
IL_0081: isinst ""C""
IL_0086: stloc.s V_13
IL_0088: ldloc.s V_13
IL_008a: brfalse.s IL_00fc
IL_008c: ldloc.s V_13
IL_008e: callvirt ""object C.P.get""
IL_0093: stloc.s V_14
IL_0095: ldloc.s V_14
IL_0097: isinst ""int""
IL_009c: brfalse.s IL_00fc
IL_009e: ldloc.s V_10
IL_00a0: ldloc.s V_14
IL_00a2: unbox.any ""int""
IL_00a7: stfld ""int C.<>c__DisplayClass0_2.<p>5__4""
IL_00ac: ldloc.s V_13
IL_00ae: callvirt ""object C.Q.get""
IL_00b3: stloc.s V_15
IL_00b5: ldloc.s V_15
IL_00b7: isinst ""C""
IL_00bc: stloc.s V_16
IL_00be: ldloc.s V_16
IL_00c0: brfalse.s IL_00fc
IL_00c2: ldloc.s V_16
IL_00c4: callvirt ""object C.P.get""
IL_00c9: stloc.s V_17
IL_00cb: ldloc.s V_17
IL_00cd: isinst ""int""
IL_00d2: brfalse.s IL_00fc
IL_00d4: ldloc.s V_10
IL_00d6: ldloc.s V_17
IL_00d8: unbox.any ""int""
IL_00dd: stfld ""int C.<>c__DisplayClass0_2.<q>5__5""
IL_00e2: br.s IL_00e4
IL_00e4: br.s IL_00e6
IL_00e6: ldloc.s V_10
IL_00e8: ldftn ""int C.<>c__DisplayClass0_2.<M>b__2()""
IL_00ee: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_00f3: call ""int C.G(System.Func<int>)""
IL_00f8: stloc.s V_11
IL_00fa: br.s IL_0102
IL_00fc: ldc.i4.s 10
IL_00fe: stloc.s V_11
IL_0100: br.s IL_0102
IL_0102: ldloc.s V_11
IL_0104: stloc.s V_18
IL_0106: ldloc.s V_18
IL_0108: stloc.s V_4
IL_010a: br IL_0190
~IL_010f: newobj ""C.<>c__DisplayClass0_3..ctor()""
IL_0114: stloc.s V_19
IL_0116: call ""object C.F()""
IL_011b: stloc.s V_17
IL_011d: ldloc.s V_17
IL_011f: isinst ""C""
IL_0124: stloc.s V_16
IL_0126: ldloc.s V_16
IL_0128: brfalse.s IL_0164
IL_012a: ldloc.s V_16
IL_012c: callvirt ""object C.P.get""
IL_0131: stloc.s V_15
IL_0133: ldloc.s V_15
IL_0135: isinst ""int""
IL_013a: brfalse.s IL_0164
IL_013c: ldloc.s V_19
IL_013e: ldloc.s V_15
IL_0140: unbox.any ""int""
IL_0145: stfld ""int C.<>c__DisplayClass0_3.<r>5__6""
IL_014a: br.s IL_014c
IL_014c: br.s IL_014e
IL_014e: ldloc.s V_19
IL_0150: ldftn ""int C.<>c__DisplayClass0_3.<M>b__3()""
IL_0156: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_015b: call ""int C.G(System.Func<int>)""
IL_0160: stloc.s V_11
IL_0162: br.s IL_016a
IL_0164: ldc.i4.s 20
IL_0166: stloc.s V_11
IL_0168: br.s IL_016a
IL_016a: ldloc.s V_11
IL_016c: stloc.s V_20
IL_016e: ldloc.s V_20
IL_0170: stloc.s V_4
IL_0172: br.s IL_0190
IL_0174: br.s IL_0176
IL_0176: ldloc.3
IL_0177: ldftn ""int C.<>c__DisplayClass0_1.<M>b__1()""
IL_017d: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_0182: call ""int C.G(System.Func<int>)""
IL_0187: stloc.s V_4
IL_0189: br.s IL_0190
IL_018b: ldc.i4.0
IL_018c: stloc.s V_4
IL_018e: br.s IL_0190
IL_0190: ldloc.s V_4
IL_0192: stloc.s V_9
IL_0194: ldloc.0
IL_0195: ldloc.s V_9
IL_0197: stfld ""int C.<>c__DisplayClass0_0.<t>5__2""
IL_019c: br.s IL_019e
IL_019e: ldloc.0
IL_019f: ldfld ""int C.<>c__DisplayClass0_0.<t>5__2""
IL_01a4: ldc.i4.0
IL_01a5: bgt.s IL_01a9
IL_01a7: br.s IL_01bd
IL_01a9: ldloc.0
IL_01aa: ldftn ""int C.<>c__DisplayClass0_0.<M>b__0()""
IL_01b0: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_01b5: call ""int C.G(System.Func<int>)""
IL_01ba: stloc.1
IL_01bb: br.s IL_01c1
IL_01bd: ldc.i4.0
IL_01be: stloc.1
IL_01bf: br.s IL_01c1
IL_01c1: ldloc.1
IL_01c2: stloc.2
IL_01c3: ldloc.2
IL_01c4: stloc.s V_21
IL_01c6: br.s IL_01c8
-IL_01c8: ldloc.s V_21
IL_01ca: ret
}");
verifier.VerifyPdb("C.M", @"
<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""C"" name=""M"">
<customDebugInfo>
<using>
<namespace usingCount=""1"" />
</using>
<encLocalSlotMap>
<slot kind=""30"" offset=""238"" />
<slot kind=""temp"" />
<slot kind=""28"" offset=""238"" />
<slot kind=""30"" offset=""238"" ordinal=""1"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""28"" offset=""238"" ordinal=""1"" />
<slot kind=""30"" offset=""63"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""28"" offset=""63"" />
<slot kind=""30"" offset=""238"" ordinal=""2"" />
<slot kind=""28"" offset=""238"" ordinal=""2"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
<encLambdaMap>
<methodOrdinal>0</methodOrdinal>
<closure offset=""238"" />
<closure offset=""238"" />
<closure offset=""63"" />
<closure offset=""238"" />
<lambda offset=""511"" closure=""0"" />
<lambda offset=""407"" closure=""1"" />
<lambda offset=""157"" closure=""2"" />
<lambda offset=""313"" closure=""3"" />
</encLambdaMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""6"" startColumn=""5"" endLine=""6"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" hidden=""true"" document=""1"" />
<entry offset=""0x7"" hidden=""true"" document=""1"" />
<entry offset=""0x71"" hidden=""true"" document=""1"" />
<entry offset=""0x10f"" hidden=""true"" document=""1"" />
<entry offset=""0x1c8"" startLine=""27"" startColumn=""5"" endLine=""27"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x1cb"">
<namespace name=""System"" />
<scope startOffset=""0x1"" endOffset=""0x1c8"">
<local name=""CS$&lt;&gt;8__locals0"" il_index=""0"" il_start=""0x1"" il_end=""0x1c8"" attributes=""0"" />
<scope startOffset=""0x7"" endOffset=""0x19c"">
<local name=""CS$&lt;&gt;8__locals1"" il_index=""3"" il_start=""0x7"" il_end=""0x19c"" attributes=""0"" />
<scope startOffset=""0x71"" endOffset=""0x10a"">
<local name=""CS$&lt;&gt;8__locals2"" il_index=""10"" il_start=""0x71"" il_end=""0x10a"" attributes=""0"" />
</scope>
<scope startOffset=""0x10f"" endOffset=""0x172"">
<local name=""CS$&lt;&gt;8__locals3"" il_index=""19"" il_start=""0x10f"" il_end=""0x172"" attributes=""0"" />
</scope>
</scope>
</scope>
</scope>
</method>
</methods>
</symbols>
");
}
[WorkItem(12378, "https://github.com/dotnet/roslyn/issues/12378")]
[WorkItem(13971, "https://github.com/dotnet/roslyn/issues/13971")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void Patterns_SwitchStatement_Constant()
{
string source =
@"class Program
{
static void M(object o)
{
switch (o)
{
case 1 when o == null:
case 4:
case 2 when o == null:
break;
case 1 when o != null:
case 5:
case 3 when o != null:
break;
default:
break;
case 1:
break;
}
switch (o)
{
case 1:
break;
default:
break;
}
switch (o)
{
default:
break;
}
}
}";
var c = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll);
CompileAndVerify(c).VerifyIL(qualifiedMethodName: "Program.M", sequencePoints: "Program.M", source: source,
expectedIL: @"{
// Code size 123 (0x7b)
.maxstack 2
.locals init (object V_0,
int V_1,
object V_2,
object V_3,
int V_4,
object V_5,
object V_6,
object V_7)
// sequence point: {
IL_0000: nop
// sequence point: switch (o)
IL_0001: ldarg.0
IL_0002: stloc.2
// sequence point: <hidden>
IL_0003: ldloc.2
IL_0004: stloc.0
// sequence point: <hidden>
IL_0005: ldloc.0
IL_0006: isinst ""int""
IL_000b: brfalse.s IL_004a
IL_000d: ldloc.0
IL_000e: unbox.any ""int""
IL_0013: stloc.1
// sequence point: <hidden>
IL_0014: ldloc.1
IL_0015: ldc.i4.1
IL_0016: sub
IL_0017: switch (
IL_0032,
IL_0037,
IL_0043,
IL_003c,
IL_0048)
IL_0030: br.s IL_004a
// sequence point: when o == null
IL_0032: ldarg.0
IL_0033: brfalse.s IL_003c
// sequence point: <hidden>
IL_0035: br.s IL_003e
// sequence point: when o == null
IL_0037: ldarg.0
IL_0038: brfalse.s IL_003c
// sequence point: <hidden>
IL_003a: br.s IL_004a
// sequence point: break;
IL_003c: br.s IL_004e
// sequence point: when o != null
IL_003e: ldarg.0
IL_003f: brtrue.s IL_0048
// sequence point: <hidden>
IL_0041: br.s IL_004c
// sequence point: when o != null
IL_0043: ldarg.0
IL_0044: brtrue.s IL_0048
// sequence point: <hidden>
IL_0046: br.s IL_004a
// sequence point: break;
IL_0048: br.s IL_004e
// sequence point: break;
IL_004a: br.s IL_004e
// sequence point: break;
IL_004c: br.s IL_004e
// sequence point: switch (o)
IL_004e: ldarg.0
IL_004f: stloc.s V_5
// sequence point: <hidden>
IL_0051: ldloc.s V_5
IL_0053: stloc.3
// sequence point: <hidden>
IL_0054: ldloc.3
IL_0055: isinst ""int""
IL_005a: brfalse.s IL_006d
IL_005c: ldloc.3
IL_005d: unbox.any ""int""
IL_0062: stloc.s V_4
// sequence point: <hidden>
IL_0064: ldloc.s V_4
IL_0066: ldc.i4.1
IL_0067: beq.s IL_006b
IL_0069: br.s IL_006d
// sequence point: break;
IL_006b: br.s IL_006f
// sequence point: break;
IL_006d: br.s IL_006f
// sequence point: switch (o)
IL_006f: ldarg.0
IL_0070: stloc.s V_7
// sequence point: <hidden>
IL_0072: ldloc.s V_7
IL_0074: stloc.s V_6
// sequence point: <hidden>
IL_0076: br.s IL_0078
// sequence point: break;
IL_0078: br.s IL_007a
// sequence point: }
IL_007a: ret
}");
c.VerifyPdb(
@"<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""Program"" name=""M"" parameterNames=""o"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<encLocalSlotMap>
<slot kind=""35"" offset=""11"" />
<slot kind=""35"" offset=""11"" ordinal=""1"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""35"" offset=""378"" ordinal=""1"" />
<slot kind=""1"" offset=""378"" />
<slot kind=""35"" offset=""511"" />
<slot kind=""1"" offset=""511"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""4"" startColumn=""5"" endLine=""4"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""5"" startColumn=""9"" endLine=""5"" endColumn=""19"" document=""1"" />
<entry offset=""0x3"" hidden=""true"" document=""1"" />
<entry offset=""0x5"" hidden=""true"" document=""1"" />
<entry offset=""0x14"" hidden=""true"" document=""1"" />
<entry offset=""0x32"" startLine=""7"" startColumn=""20"" endLine=""7"" endColumn=""34"" document=""1"" />
<entry offset=""0x35"" hidden=""true"" document=""1"" />
<entry offset=""0x37"" startLine=""9"" startColumn=""20"" endLine=""9"" endColumn=""34"" document=""1"" />
<entry offset=""0x3a"" hidden=""true"" document=""1"" />
<entry offset=""0x3c"" startLine=""10"" startColumn=""17"" endLine=""10"" endColumn=""23"" document=""1"" />
<entry offset=""0x3e"" startLine=""11"" startColumn=""20"" endLine=""11"" endColumn=""34"" document=""1"" />
<entry offset=""0x41"" hidden=""true"" document=""1"" />
<entry offset=""0x43"" startLine=""13"" startColumn=""20"" endLine=""13"" endColumn=""34"" document=""1"" />
<entry offset=""0x46"" hidden=""true"" document=""1"" />
<entry offset=""0x48"" startLine=""14"" startColumn=""17"" endLine=""14"" endColumn=""23"" document=""1"" />
<entry offset=""0x4a"" startLine=""16"" startColumn=""17"" endLine=""16"" endColumn=""23"" document=""1"" />
<entry offset=""0x4c"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" document=""1"" />
<entry offset=""0x4e"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" document=""1"" />
<entry offset=""0x51"" hidden=""true"" document=""1"" />
<entry offset=""0x54"" hidden=""true"" document=""1"" />
<entry offset=""0x64"" hidden=""true"" document=""1"" />
<entry offset=""0x6b"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" document=""1"" />
<entry offset=""0x6d"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" document=""1"" />
<entry offset=""0x6f"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" document=""1"" />
<entry offset=""0x72"" hidden=""true"" document=""1"" />
<entry offset=""0x76"" hidden=""true"" document=""1"" />
<entry offset=""0x78"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" document=""1"" />
<entry offset=""0x7a"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" document=""1"" />
</sequencePoints>
</method>
</methods>
</symbols>");
}
[WorkItem(37172, "https://github.com/dotnet/roslyn/issues/37172")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void Patterns_SwitchStatement_Tuple()
{
string source = @"
public class C
{
static int F(int i)
{
switch (G())
{
case (1, 2): return 3;
default: return 0;
};
}
static (object, object) G() => (2, 3);
}";
var c = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll, references: s_valueTupleRefs);
var cv = CompileAndVerify(c);
cv.VerifyIL("C.F", @"
{
// Code size 80 (0x50)
.maxstack 2
.locals init (System.ValueTuple<object, object> V_0,
object V_1,
int V_2,
object V_3,
int V_4,
System.ValueTuple<object, object> V_5,
int V_6)
IL_0000: nop
IL_0001: call ""(object, object) C.G()""
IL_0006: stloc.s V_5
IL_0008: ldloc.s V_5
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: ldfld ""object System.ValueTuple<object, object>.Item1""
IL_0011: stloc.1
IL_0012: ldloc.1
IL_0013: isinst ""int""
IL_0018: brfalse.s IL_0048
IL_001a: ldloc.1
IL_001b: unbox.any ""int""
IL_0020: stloc.2
IL_0021: ldloc.2
IL_0022: ldc.i4.1
IL_0023: bne.un.s IL_0048
IL_0025: ldloc.0
IL_0026: ldfld ""object System.ValueTuple<object, object>.Item2""
IL_002b: stloc.3
IL_002c: ldloc.3
IL_002d: isinst ""int""
IL_0032: brfalse.s IL_0048
IL_0034: ldloc.3
IL_0035: unbox.any ""int""
IL_003a: stloc.s V_4
IL_003c: ldloc.s V_4
IL_003e: ldc.i4.2
IL_003f: beq.s IL_0043
IL_0041: br.s IL_0048
IL_0043: ldc.i4.3
IL_0044: stloc.s V_6
IL_0046: br.s IL_004d
IL_0048: ldc.i4.0
IL_0049: stloc.s V_6
IL_004b: br.s IL_004d
IL_004d: ldloc.s V_6
IL_004f: ret
}
");
c.VerifyPdb("C.F", @"
<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""C"" name=""F"" parameterNames=""i"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<encLocalSlotMap>
<slot kind=""0"" offset=""12"" />
<slot kind=""35"" offset=""17"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""35"" offset=""11"" ordinal=""1"" />
<slot kind=""35"" offset=""11"" ordinal=""2"" />
<slot kind=""35"" offset=""11"" ordinal=""3"" />
<slot kind=""35"" offset=""11"" ordinal=""4"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""21"" offset=""0"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""1"" startColumn=""31"" endLine=""1"" endColumn=""64"" document=""1"" />
<entry offset=""0x0"" startLine=""5"" startColumn=""5"" endLine=""5"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""6"" startColumn=""9"" endLine=""6"" endColumn=""21"" document=""1"" />
<entry offset=""0x8"" hidden=""true"" document=""1"" />
<entry offset=""0xb"" hidden=""true"" document=""1"" />
<entry offset=""0x12"" hidden=""true"" document=""1"" />
<entry offset=""0x21"" hidden=""true"" document=""1"" />
<entry offset=""0x2c"" hidden=""true"" document=""1"" />
<entry offset=""0x3c"" hidden=""true"" document=""1"" />
<entry offset=""0x43"" startLine=""8"" startColumn=""26"" endLine=""8"" endColumn=""35"" document=""1"" />
<entry offset=""0x48"" startLine=""9"" startColumn=""22"" endLine=""9"" endColumn=""31"" document=""1"" />
<entry offset=""0x4d"" startLine=""11"" startColumn=""5"" endLine=""11"" endColumn=""6"" document=""1"" />
</sequencePoints>
<scope startOffset=""0x0"" endOffset=""0x2f"">
<local name=""i"" il_index=""0"" il_start=""0x0"" il_end=""0x2f"" attributes=""0"" />
</scope>
</method>
</methods>
</symbols>");
......@@ -7962,6 +9551,44 @@ static int F(System.Func<int> x)
var c = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll);
var v = CompileAndVerify(c);
v.VerifyIL("C..ctor", sequencePoints: "C..ctor", expectedIL: @"
{
// Code size 90 (0x5a)
.maxstack 4
.locals init (C.<>c__DisplayClass4_0 V_0, //CS$<>8__locals0
C.<>c__DisplayClass4_1 V_1) //CS$<>8__locals1
~IL_0000: newobj ""C.<>c__DisplayClass4_0..ctor()""
IL_0005: stloc.0
-IL_0006: ldarg.0
IL_0007: ldloc.0
IL_0008: ldflda ""int C.<>c__DisplayClass4_0.z""
IL_000d: call ""int C.G(out int)""
IL_0012: ldloc.0
IL_0013: ldftn ""int C.<>c__DisplayClass4_0.<.ctor>b__0()""
IL_0019: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_001e: call ""int C.F(System.Func<int>)""
IL_0023: add
IL_0024: stfld ""int C.y1""
~IL_0029: newobj ""C.<>c__DisplayClass4_1..ctor()""
IL_002e: stloc.1
-IL_002f: ldarg.0
IL_0030: ldloc.1
IL_0031: ldflda ""int C.<>c__DisplayClass4_1.u""
IL_0036: call ""int C.G(out int)""
IL_003b: ldloc.1
IL_003c: ldftn ""int C.<>c__DisplayClass4_1.<.ctor>b__1()""
IL_0042: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_0047: call ""int C.F(System.Func<int>)""
IL_004c: add
IL_004d: stfld ""int C.y2""
IL_0052: ldarg.0
IL_0053: call ""object..ctor()""
IL_0058: nop
IL_0059: ret
}
");
c.VerifyPdb("C..ctor", @"
<symbols>
<files>
......@@ -8338,11 +9965,11 @@ public void SyntaxOffset_OutVarInSwitchExpresison()
</using>
<encLocalSlotMap>
<slot kind=""0"" offset=""13"" />
<slot kind=""35"" offset=""3"" />
<slot kind=""35"" offset=""3"" />
<slot kind=""28"" offset=""33"" />
<slot kind=""35"" offset=""33"" />
<slot kind=""28"" offset=""33"" ordinal=""1"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""28"" offset=""35"" />
<slot kind=""temp"" />
<slot kind=""28"" offset=""35"" ordinal=""1"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
......@@ -8470,204 +10097,6 @@ partial class C
");
}
[WorkItem(12378, "https://github.com/dotnet/roslyn/issues/12378")]
[WorkItem(13971, "https://github.com/dotnet/roslyn/issues/13971")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void PatternSwitchSequencePoints()
{
string source =
@"class Program
{
static void M(object o)
{
switch (o)
{
case 1 when o == null:
case 4:
case 2 when o == null:
break;
case 1 when o != null:
case 5:
case 3 when o != null:
break;
default:
break;
case 1:
break;
}
switch (o)
{
case 1:
break;
default:
break;
}
switch (o)
{
default:
break;
}
}
}";
var c = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll);
CompileAndVerify(c).VerifyIL(qualifiedMethodName: "Program.M", sequencePoints: "Program.M", source: source,
expectedIL: @"{
// Code size 123 (0x7b)
.maxstack 2
.locals init (object V_0,
int V_1,
object V_2,
object V_3,
int V_4,
object V_5,
object V_6,
object V_7)
// sequence point: {
IL_0000: nop
// sequence point: switch (o)
IL_0001: ldarg.0
IL_0002: stloc.2
// sequence point: <hidden>
IL_0003: ldloc.2
IL_0004: stloc.0
// sequence point: <hidden>
IL_0005: ldloc.0
IL_0006: isinst ""int""
IL_000b: brfalse.s IL_004a
IL_000d: ldloc.0
IL_000e: unbox.any ""int""
IL_0013: stloc.1
// sequence point: <hidden>
IL_0014: ldloc.1
IL_0015: ldc.i4.1
IL_0016: sub
IL_0017: switch (
IL_0032,
IL_0037,
IL_0043,
IL_003c,
IL_0048)
IL_0030: br.s IL_004a
// sequence point: when o == null
IL_0032: ldarg.0
IL_0033: brfalse.s IL_003c
// sequence point: <hidden>
IL_0035: br.s IL_003e
// sequence point: when o == null
IL_0037: ldarg.0
IL_0038: brfalse.s IL_003c
// sequence point: <hidden>
IL_003a: br.s IL_004a
// sequence point: break;
IL_003c: br.s IL_004e
// sequence point: when o != null
IL_003e: ldarg.0
IL_003f: brtrue.s IL_0048
// sequence point: <hidden>
IL_0041: br.s IL_004c
// sequence point: when o != null
IL_0043: ldarg.0
IL_0044: brtrue.s IL_0048
// sequence point: <hidden>
IL_0046: br.s IL_004a
// sequence point: break;
IL_0048: br.s IL_004e
// sequence point: break;
IL_004a: br.s IL_004e
// sequence point: break;
IL_004c: br.s IL_004e
// sequence point: switch (o)
IL_004e: ldarg.0
IL_004f: stloc.s V_5
// sequence point: <hidden>
IL_0051: ldloc.s V_5
IL_0053: stloc.3
// sequence point: <hidden>
IL_0054: ldloc.3
IL_0055: isinst ""int""
IL_005a: brfalse.s IL_006d
IL_005c: ldloc.3
IL_005d: unbox.any ""int""
IL_0062: stloc.s V_4
// sequence point: <hidden>
IL_0064: ldloc.s V_4
IL_0066: ldc.i4.1
IL_0067: beq.s IL_006b
IL_0069: br.s IL_006d
// sequence point: break;
IL_006b: br.s IL_006f
// sequence point: break;
IL_006d: br.s IL_006f
// sequence point: switch (o)
IL_006f: ldarg.0
IL_0070: stloc.s V_7
// sequence point: <hidden>
IL_0072: ldloc.s V_7
IL_0074: stloc.s V_6
// sequence point: <hidden>
IL_0076: br.s IL_0078
// sequence point: break;
IL_0078: br.s IL_007a
// sequence point: }
IL_007a: ret
}");
c.VerifyPdb(
@"<symbols>
<files>
<file id=""1"" name="""" language=""C#"" />
</files>
<methods>
<method containingType=""Program"" name=""M"" parameterNames=""o"">
<customDebugInfo>
<using>
<namespace usingCount=""0"" />
</using>
<encLocalSlotMap>
<slot kind=""35"" offset=""11"" />
<slot kind=""35"" offset=""11"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""1"" offset=""378"" />
<slot kind=""35"" offset=""511"" />
<slot kind=""1"" offset=""511"" />
</encLocalSlotMap>
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""4"" startColumn=""5"" endLine=""4"" endColumn=""6"" document=""1"" />
<entry offset=""0x1"" startLine=""5"" startColumn=""9"" endLine=""5"" endColumn=""19"" document=""1"" />
<entry offset=""0x3"" hidden=""true"" document=""1"" />
<entry offset=""0x5"" hidden=""true"" document=""1"" />
<entry offset=""0x14"" hidden=""true"" document=""1"" />
<entry offset=""0x32"" startLine=""7"" startColumn=""20"" endLine=""7"" endColumn=""34"" document=""1"" />
<entry offset=""0x35"" hidden=""true"" document=""1"" />
<entry offset=""0x37"" startLine=""9"" startColumn=""20"" endLine=""9"" endColumn=""34"" document=""1"" />
<entry offset=""0x3a"" hidden=""true"" document=""1"" />
<entry offset=""0x3c"" startLine=""10"" startColumn=""17"" endLine=""10"" endColumn=""23"" document=""1"" />
<entry offset=""0x3e"" startLine=""11"" startColumn=""20"" endLine=""11"" endColumn=""34"" document=""1"" />
<entry offset=""0x41"" hidden=""true"" document=""1"" />
<entry offset=""0x43"" startLine=""13"" startColumn=""20"" endLine=""13"" endColumn=""34"" document=""1"" />
<entry offset=""0x46"" hidden=""true"" document=""1"" />
<entry offset=""0x48"" startLine=""14"" startColumn=""17"" endLine=""14"" endColumn=""23"" document=""1"" />
<entry offset=""0x4a"" startLine=""16"" startColumn=""17"" endLine=""16"" endColumn=""23"" document=""1"" />
<entry offset=""0x4c"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" document=""1"" />
<entry offset=""0x4e"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" document=""1"" />
<entry offset=""0x51"" hidden=""true"" document=""1"" />
<entry offset=""0x54"" hidden=""true"" document=""1"" />
<entry offset=""0x64"" hidden=""true"" document=""1"" />
<entry offset=""0x6b"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" document=""1"" />
<entry offset=""0x6d"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" document=""1"" />
<entry offset=""0x6f"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" document=""1"" />
<entry offset=""0x72"" hidden=""true"" document=""1"" />
<entry offset=""0x76"" hidden=""true"" document=""1"" />
<entry offset=""0x78"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" document=""1"" />
<entry offset=""0x7a"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" document=""1"" />
</sequencePoints>
</method>
</methods>
</symbols>");
}
[WorkItem(14437, "https://github.com/dotnet/roslyn/issues/14437")]
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
public void LocalFunctionSequencePoints()
......
......@@ -92,12 +92,12 @@ internal sealed class EncVariableSlotAllocator : VariableSlotAllocator
public override DebugId? MethodId => _methodId;
private int CalculateSyntaxOffsetInPreviousMethod(int position, SyntaxTree tree)
private int CalculateSyntaxOffsetInPreviousMethod(SyntaxNode node)
{
// Note that syntax offset of a syntax node contained in a lambda body is calculated by the containing top-level method,
// not by the lambda method. The offset is thus relative to the top-level method body start. We can thus avoid mapping
// the current lambda symbol or body to the corresponding previous lambda symbol or body, which is non-trivial.
return _previousTopLevelMethod.CalculateLocalSyntaxOffset(position, tree);
return _previousTopLevelMethod.CalculateLocalSyntaxOffset(_lambdaSyntaxFacts.GetDeclaratorPosition(node), node.SyntaxTree);
}
public override void AddPreviousLocals(ArrayBuilder<Cci.ILocalDefinition> builder)
......@@ -120,11 +120,11 @@ private bool TryGetPreviousLocalId(SyntaxNode currentDeclarator, LocalDebugId cu
SyntaxNode previousDeclarator = _syntaxMapOpt(currentDeclarator);
if (previousDeclarator == null)
{
previousId = default(LocalDebugId);
previousId = default;
return false;
}
int syntaxOffset = CalculateSyntaxOffsetInPreviousMethod(previousDeclarator.SpanStart, previousDeclarator.SyntaxTree);
int syntaxOffset = CalculateSyntaxOffsetInPreviousMethod(previousDeclarator);
previousId = new LocalDebugId(syntaxOffset, currentId.Ordinal);
return true;
}
......@@ -246,7 +246,7 @@ private bool TryGetPreviousSyntaxOffset(SyntaxNode currentSyntax, out int previo
return false;
}
previousSyntaxOffset = CalculateSyntaxOffsetInPreviousMethod(previousSyntax.SpanStart, previousSyntax.SyntaxTree);
previousSyntaxOffset = CalculateSyntaxOffsetInPreviousMethod(previousSyntax);
return true;
}
......@@ -284,7 +284,7 @@ private bool TryGetPreviousLambdaSyntaxOffset(SyntaxNode lambdaOrLambdaBodySynta
previousSyntax = previousLambdaSyntax;
}
previousSyntaxOffset = CalculateSyntaxOffsetInPreviousMethod(previousSyntax.SpanStart, previousSyntax.SyntaxTree);
previousSyntaxOffset = CalculateSyntaxOffsetInPreviousMethod(previousSyntax);
return true;
}
......
......@@ -15,5 +15,11 @@ internal abstract class LambdaSyntaxFacts
/// JoinClause1.GetCorrespondingLambdaBody(JoinClause2.RightExpression) returns JoinClause1.RightExpression.
/// </summary>
public abstract SyntaxNode TryGetCorrespondingLambdaBody(SyntaxNode previousLambdaSyntax, SyntaxNode lambdaOrLambdaBodySyntax);
/// <summary>
/// Given a node that represents a variable declaration, lambda or a closure scope return the position to be used to calculate
/// the node's syntax offset with respect to its containing member.
/// </summary>
public abstract int GetDeclaratorPosition(SyntaxNode node);
}
}
......@@ -18,5 +18,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
Public Overrides Function TryGetCorrespondingLambdaBody(previousLambdaSyntax As SyntaxNode, lambdaOrLambdaBodySyntax As SyntaxNode) As SyntaxNode
Return LambdaUtilities.GetCorrespondingLambdaBody(lambdaOrLambdaBodySyntax, previousLambdaSyntax)
End Function
Public Overrides Function GetDeclaratorPosition(node As SyntaxNode) As Integer
Return node.SpanStart
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册