diff --git a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs index cffc7949283de..120634338bcb9 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs @@ -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( diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs index e5ef0f94da3ef..ea1d28693f917 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitStatement.cs @@ -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); diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpLambdaSyntaxFacts.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpLambdaSyntaxFacts.cs index 8726ba5731b77..50b3048fe16eb 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpLambdaSyntaxFacts.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpLambdaSyntaxFacts.cs @@ -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); } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs index b85a9206fb1c8..ba18003f89fc1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs @@ -535,7 +535,7 @@ internal DebugId GetClosureId(SyntaxNode syntax, ArrayBuilder 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; diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs index cd7217a3418cc..f8a018fd0ce01 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs @@ -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; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs index a261a0e3fe16e..016dda05fc8fd 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs @@ -17,7 +17,7 @@ internal partial class LocalRewriter /// /// A common base class for lowering the pattern switch statement and the pattern switch expression. /// - private class BaseSwitchLocalRewriter : PatternLocalRewriter + private abstract class BaseSwitchLocalRewriter : PatternLocalRewriter { /// /// 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 /// private readonly PooledDictionary _dagNodeLabels = PooledDictionary.GetInstance(); - /// - /// 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). - /// - private readonly bool _isSwitchStatement; - protected BaseSwitchLocalRewriter( SyntaxNode node, LocalRewriter localRewriter, - ImmutableArray arms, - bool isSwitchStatement) + ImmutableArray arms) : base(node, localRewriter) { - this._isSwitchStatement = isSwitchStatement; foreach (var arm in arms) { var armBuilder = ArrayBuilder.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 @@ protected BoundDecisionDag ShareTempsIfPossibleAndEvaluateInput( // 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 @@ protected BoundDecisionDag ShareTempsIfPossibleAndEvaluateInput( Debug.Assert(this._loweredDecisionDag.IsEmpty()); ComputeLabelSet(decisionDag); LowerDecisionDagCore(decisionDag); - ImmutableArray loweredDag = this._loweredDecisionDag.ToImmutableAndFree(); - ImmutableDictionary> switchSections = this._switchArms.ToImmutableDictionary(kv => kv.Key, kv => kv.Value.ToImmutableAndFree()); - this._switchArms.Clear(); + ImmutableArray 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) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs index 6f4a12891d2d2..8349b648b8a93 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs @@ -18,7 +18,7 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node return result; } - private class IsPatternExpressionLocalRewriter : PatternLocalRewriter + private sealed class IsPatternExpressionLocalRewriter : PatternLocalRewriter { /// /// 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.GetInstance(); - this._sideEffectBuilder = ArrayBuilder.GetInstance(); + _conjunctBuilder = ArrayBuilder.GetInstance(); + _sideEffectBuilder = ArrayBuilder.GetInstance(); } + protected override bool IsSwitchStatement => false; + public new void Free() { _conjunctBuilder.Free(); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs index 9011acf3a6da1..ea40c6a50ef16 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs @@ -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 { + /// + /// A map from section syntax to the first label in that section. + /// + private readonly Dictionary _sectionLabels = PooledDictionary.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; } - /// - /// A map from section syntax to the first label in that section. - /// - private Dictionary _sectionLabels = PooledDictionary.GetInstance(); - /// /// 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)) { } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs index b462b1cbe0e48..663ef9b172bce 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs @@ -16,7 +16,7 @@ internal sealed partial class LocalRewriter /// /// A common base class for lowering constructs that use pattern-matching. /// - 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); } + /// + /// 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. + /// + protected abstract bool IsSwitchStatement { get; } + public void Free() { _tempAllocator.Free(); } - public class DagTempAllocator + public sealed class DagTempAllocator { private readonly SyntheticBoundNodeFactory _factory; private readonly PooledDictionary _map = PooledDictionary.GetInstance(); private readonly ArrayBuilder _temps = ArrayBuilder.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); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs index d59991b47fa1e..aed4004c80fc5 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs @@ -20,14 +20,16 @@ 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)) { } - public static BoundExpression Rewrite(LocalRewriter localRewriter, BoundConvertedSwitchExpression node) + protected override bool IsSwitchStatement => false; + + public static BoundExpression Rewrite(LocalRewriter localRewriter, BoundSwitchExpression node) { var rewriter = new SwitchExpressionLocalRewriter(node, localRewriter); BoundExpression result = rewriter.LowerSwitchExpression(node); @@ -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) { diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index d7b7a9d014fc2..209c8eed4aea1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -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. diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index fc0a3366fad44..f27814b218202 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -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 { diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs index 37ec9d6f2ed50..bd971f62975c7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs @@ -175,7 +175,7 @@ private void CreateNonReusableLocalProxies( // 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); diff --git a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs index 440b91ae37496..864950d09fee3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs @@ -460,6 +460,16 @@ internal static bool IsClosureScope(SyntaxNode node) return false; } + /// + /// 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. + /// + 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; diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index ee386035a242d..82ef81c53c1d4 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -7288,31 +7288,1624 @@ 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", @" - + + + + + + + + + + + + + + + + + +"); + } + + [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); + + // TODO: https://github.com/dotnet/roslyn/issues/37172 + // Synthesized temp variables used for patterns have the same type, syntax offset and kind. + // This breaks Enc mapping. + + 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: + IL_0008: ldloc.s V_19 + IL_000a: stloc.s V_7 + // sequence point: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + IL_011a: ldloc.s V_10 + IL_011c: callvirt ""int D.P.get"" + IL_0121: stloc.s V_15 + // sequence point: + 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: + 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: + 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: + 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: + 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: + 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: + 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: + IL_018b: br IL_006e + // sequence point: when B() + IL_0190: call ""bool Program.B()"" + IL_0195: brtrue.s IL_019c + // sequence point: + 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: + 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: + 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: + 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", @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + + [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 not 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", @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + + [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", @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + + [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 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.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.

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.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.b__2()"" + IL_00ee: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_00f3: call ""int C.G(System.Func)"" + 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.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.b__3()"" + IL_0156: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_015b: call ""int C.G(System.Func)"" + 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.b__1()"" + IL_017d: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0182: call ""int C.G(System.Func)"" + 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.5__2"" + IL_019c: br.s IL_019e + IL_019e: ldloc.0 + IL_019f: ldfld ""int C.<>c__DisplayClass0_0.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.b__0()"" + IL_01b0: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_01b5: call ""int C.G(System.Func)"" + 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", @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + + [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: + IL_0003: ldloc.2 + IL_0004: stloc.0 + // sequence point: + 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: + 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: + IL_0035: br.s IL_003e + // sequence point: when o == null + IL_0037: ldarg.0 + IL_0038: brfalse.s IL_003c + // sequence point: + 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: + IL_0041: br.s IL_004c + // sequence point: when o != null + IL_0043: ldarg.0 + IL_0044: brtrue.s IL_0048 + // sequence point: + 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: + IL_0051: ldloc.s V_5 + IL_0053: stloc.3 + // sequence point: + 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: + 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: + IL_0072: ldloc.s V_7 + IL_0074: stloc.s V_6 + // sequence point: + IL_0076: br.s IL_0078 + // sequence point: break; + IL_0078: br.s IL_007a + // sequence point: } + IL_007a: ret +}"); + c.VerifyPdb( +@" + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + + [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 V_0, + object V_1, + int V_2, + object V_3, + int V_4, + System.ValueTuple 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.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.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", @" + + + + + + - - + + + + + + + - + + + - - - "); @@ -7962,6 +9555,44 @@ static int F(System.Func 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..ctor(object, System.IntPtr)"" + IL_001e: call ""int C.F(System.Func)"" + 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..ctor(object, System.IntPtr)"" + IL_0047: call ""int C.F(System.Func)"" + 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", @" @@ -8338,11 +9969,11 @@ public void SyntaxOffset_OutVarInSwitchExpresison() - - - - - + + + + + @@ -8470,204 +10101,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: - IL_0003: ldloc.2 - IL_0004: stloc.0 - // sequence point: - 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: - 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: - IL_0035: br.s IL_003e - // sequence point: when o == null - IL_0037: ldarg.0 - IL_0038: brfalse.s IL_003c - // sequence point: - 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: - IL_0041: br.s IL_004c - // sequence point: when o != null - IL_0043: ldarg.0 - IL_0044: brtrue.s IL_0048 - // sequence point: - 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: - IL_0051: ldloc.s V_5 - IL_0053: stloc.3 - // sequence point: - 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: - 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: - IL_0072: ldloc.s V_7 - IL_0074: stloc.s V_6 - // sequence point: - IL_0076: br.s IL_0078 - // sequence point: break; - IL_0078: br.s IL_007a - // sequence point: } - IL_007a: ret -}"); - c.VerifyPdb( -@" - - - - - - - - - - - - - - - - - - - - - - - - - - -"); - } - [WorkItem(14437, "https://github.com/dotnet/roslyn/issues/14437")] [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] public void LocalFunctionSequencePoints() diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs index faca23d8e3b6b..29b736989549b 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs @@ -92,12 +92,12 @@ public EncVariableSlotAllocator( 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 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; } diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/LambdaSyntaxFacts.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/LambdaSyntaxFacts.cs index 5ccaaaaebcace..b585d2aba893e 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/LambdaSyntaxFacts.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/LambdaSyntaxFacts.cs @@ -15,5 +15,11 @@ internal abstract class LambdaSyntaxFacts /// JoinClause1.GetCorrespondingLambdaBody(JoinClause2.RightExpression) returns JoinClause1.RightExpression. ///

public abstract SyntaxNode TryGetCorrespondingLambdaBody(SyntaxNode previousLambdaSyntax, SyntaxNode lambdaOrLambdaBodySyntax); + + /// + /// 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. + /// + public abstract int GetDeclaratorPosition(SyntaxNode node); } } diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicLambdaSyntaxFacts.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicLambdaSyntaxFacts.vb index abdb4ec684ce4..571353970c39c 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicLambdaSyntaxFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicLambdaSyntaxFacts.vb @@ -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