diff --git a/docs/contributing/Compiler Test Plan.md b/docs/contributing/Compiler Test Plan.md
index ffa51afae2e65..8b7bc70c3ed7e 100644
--- a/docs/contributing/Compiler Test Plan.md
+++ b/docs/contributing/Compiler Test Plan.md
@@ -347,6 +347,12 @@ __makeref( x )
- Declaration Pattern
- Constant Pattern
- Recursive Pattern
+- Parenthesized Pattern
+- `and` Pattern
+- `or` Pattern
+- `not` Pattern
+- Relational Pattern
+- Type Pattern
## Metadata table numbers / token prefixes
diff --git a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/CompoundInstrumenter.cs b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/CompoundInstrumenter.cs
index fcffd35d2df64..e151bae961723 100644
--- a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/CompoundInstrumenter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/CompoundInstrumenter.cs
@@ -186,6 +186,11 @@ public override BoundExpression InstrumentSwitchStatementExpression(BoundStateme
return Previous.InstrumentSwitchStatementExpression(original, rewrittenExpression, factory);
}
+ public override BoundExpression InstrumentSwitchExpressionArmExpression(BoundExpression original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory)
+ {
+ return Previous.InstrumentSwitchExpressionArmExpression(original, rewrittenExpression, factory);
+ }
+
public override BoundStatement InstrumentSwitchBindCasePatternVariables(BoundStatement bindings)
{
return Previous.InstrumentSwitchBindCasePatternVariables(bindings);
diff --git a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DebugInfoInjector.cs b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DebugInfoInjector.cs
index 23db7bba0b292..2eab00173db9c 100644
--- a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DebugInfoInjector.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/DebugInfoInjector.cs
@@ -406,6 +406,11 @@ public override BoundExpression InstrumentSwitchStatementExpression(BoundStateme
return AddConditionSequencePoint(base.InstrumentSwitchStatementExpression(original, rewrittenExpression, factory), original.Syntax, factory);
}
+ public override BoundExpression InstrumentSwitchExpressionArmExpression(BoundExpression original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory)
+ {
+ return new BoundSequencePointExpression(original.Syntax, base.InstrumentSwitchExpressionArmExpression(original, rewrittenExpression, factory), rewrittenExpression.Type);
+ }
+
public override BoundStatement InstrumentSwitchBindCasePatternVariables(BoundStatement bindings)
{
// Mark the code that binds pattern variables to their values as hidden.
diff --git a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/Instrumenter.cs b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/Instrumenter.cs
index 840b5c56282f6..c0e14bfd94f28 100644
--- a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/Instrumenter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/Instrumenter.cs
@@ -265,6 +265,15 @@ public virtual BoundExpression InstrumentSwitchStatementExpression(BoundStatemen
return rewrittenExpression;
}
+ ///
+ /// Instrument the expression of a switch arm of a switch expression.
+ ///
+ public virtual BoundExpression InstrumentSwitchExpressionArmExpression(BoundExpression original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory)
+ {
+ Debug.Assert(factory != null);
+ return rewrittenExpression;
+ }
+
public virtual BoundStatement InstrumentSwitchBindCasePatternVariables(BoundStatement bindings)
{
return bindings;
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.DecisionDagRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.DecisionDagRewriter.cs
index 1dd5cf4b72461..4d2b12e577b8f 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.DecisionDagRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.DecisionDagRewriter.cs
@@ -40,8 +40,9 @@ private abstract partial class DecisionDagRewriter : PatternLocalRewriter
protected DecisionDagRewriter(
SyntaxNode node,
- LocalRewriter localRewriter)
- : base(node, localRewriter)
+ LocalRewriter localRewriter,
+ bool generateInstrumentation)
+ : base(node, localRewriter, generateInstrumentation)
{
}
@@ -893,7 +894,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 (GenerateSequencePoints && !whenClause.WhenExpression.WasCompilerGenerated)
+ if (GenerateInstrumentation && !whenClause.WhenExpression.WasCompilerGenerated)
{
conditionalGoto = _localRewriter._instrumenter.InstrumentSwitchWhenClauseConditionalGotoBody(whenClause.WhenExpression, conditionalGoto);
}
@@ -904,7 +905,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(GenerateSequencePoints ? _factory.HiddenSequencePoint(jump) : jump);
+ sectionBuilder.Add(GenerateInstrumentation ? _factory.HiddenSequencePoint(jump) : jump);
}
else
{
@@ -930,7 +931,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 (GenerateSequencePoints)
+ if (GenerateInstrumentation)
_loweredDecisionDag.Add(_factory.HiddenSequencePoint());
if (nextNode != evaluationNode.Next)
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs
index c7278b4df970c..da9ca6e95398b 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs
@@ -25,21 +25,23 @@ private abstract class PatternLocalRewriter
protected readonly SyntheticBoundNodeFactory _factory;
protected readonly DagTempAllocator _tempAllocator;
- public PatternLocalRewriter(SyntaxNode node, LocalRewriter localRewriter)
+ public PatternLocalRewriter(SyntaxNode node, LocalRewriter localRewriter, bool generateInstrumentation)
{
_localRewriter = localRewriter;
_factory = localRewriter._factory;
- _tempAllocator = new DagTempAllocator(_factory, node, GenerateSequencePoints);
+ GenerateInstrumentation = generateInstrumentation;
+ _tempAllocator = new DagTempAllocator(_factory, node, generateInstrumentation);
}
///
- /// 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.
+ /// True if we should produce instrumentation and sequence points, which we do for a switch statement and a switch expression.
+ /// This affects
+ /// - whether or not we invoke the instrumentation APIs
+ /// - production of sequence points
/// - 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 GenerateSequencePoints { get; }
+ protected bool GenerateInstrumentation { get; }
public void Free()
{
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
index 6f3b1d2ce5273..23d9704c75d00 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BasePatternSwitchLocalRewriter.cs
@@ -39,8 +39,9 @@ protected override ArrayBuilder BuilderForSection(SyntaxNode whe
protected BaseSwitchLocalRewriter(
SyntaxNode node,
LocalRewriter localRewriter,
- ImmutableArray arms)
- : base(node, localRewriter)
+ ImmutableArray arms,
+ bool generateInstrumentation)
+ : base(node, localRewriter, generateInstrumentation)
{
foreach (var arm in arms)
{
@@ -48,7 +49,7 @@ protected BaseSwitchLocalRewriter(
// 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 (GenerateSequencePoints)
+ if (GenerateInstrumentation)
armBuilder.Add(_factory.HiddenSequencePoint());
_switchArms.Add(arm, armBuilder);
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs
index d6ea37f359590..9f4d949b5f43d 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs
@@ -86,14 +86,14 @@ private sealed class IsPatternExpressionGeneralLocalRewriter : DecisionDagRewrit
{
private readonly ArrayBuilder _statements = ArrayBuilder.GetInstance();
- public IsPatternExpressionGeneralLocalRewriter(SyntaxNode node, LocalRewriter localRewriter) : base(node, localRewriter)
+ public IsPatternExpressionGeneralLocalRewriter(
+ SyntaxNode node,
+ LocalRewriter localRewriter) : base(node, localRewriter, generateInstrumentation: false)
{
}
protected override ArrayBuilder BuilderForSection(SyntaxNode section) => _statements;
- protected override bool GenerateSequencePoints => false;
-
public new void Free()
{
base.Free();
@@ -151,14 +151,12 @@ private sealed class IsPatternExpressionLinearLocalRewriter : PatternLocalRewrit
private readonly ArrayBuilder _conjunctBuilder;
public IsPatternExpressionLinearLocalRewriter(BoundIsPatternExpression node, LocalRewriter localRewriter)
- : base(node.Syntax, localRewriter)
+ : base(node.Syntax, localRewriter, generateInstrumentation: false)
{
_conjunctBuilder = ArrayBuilder.GetInstance();
_sideEffectBuilder = ArrayBuilder.GetInstance();
}
- protected override bool GenerateSequencePoints => 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 dd8d53035739e..6e0dfd9ac034a 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs
@@ -26,8 +26,6 @@ private sealed class SwitchStatementLocalRewriter : BaseSwitchLocalRewriter
///
private readonly Dictionary _sectionLabels = PooledDictionary.GetInstance();
- protected override bool GenerateSequencePoints => true;
-
public static BoundStatement Rewrite(LocalRewriter localRewriter, BoundSwitchStatement node)
{
var rewriter = new SwitchStatementLocalRewriter(node, localRewriter);
@@ -65,7 +63,9 @@ protected override LabelSymbol GetDagNodeLabel(BoundDecisionDagNode dag)
}
private SwitchStatementLocalRewriter(BoundSwitchStatement node, LocalRewriter localRewriter)
- : base(node.Syntax, localRewriter, node.SwitchSections.SelectAsArray(section => section.Syntax))
+ : base(node.Syntax, localRewriter, node.SwitchSections.SelectAsArray(section => section.Syntax),
+ // Only add instrumentation (such as sequence points) if the node is not compiler-generated.
+ generateInstrumentation: localRewriter.Instrument && !node.WasCompilerGenerated)
{
}
@@ -101,7 +101,7 @@ private BoundStatement LowerSwitchStatement(BoundSwitchStatement node)
// 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 (GenerateSequencePoints)
+ if (GenerateInstrumentation)
{
// Since there may have been no code to evaluate the input, add a no-op for any previous sequence point to bind to.
if (result.Count == 0)
@@ -161,15 +161,14 @@ private BoundStatement LowerSwitchStatement(BoundSwitchStatement node)
outerVariables.AddRange(_tempAllocator.AllTemps());
_factory.Syntax = node.Syntax;
- result.Add(_factory.HiddenSequencePoint());
+ if (GenerateInstrumentation)
+ result.Add(_factory.HiddenSequencePoint());
+
result.Add(_factory.Label(node.BreakLabel));
BoundStatement translatedSwitch = _factory.Block(outerVariables.ToImmutableAndFree(), node.InnerLocalFunctions, result.ToImmutableAndFree());
- // Only add instrumentation (such as a sequence point) if the node is not compiler-generated.
- if (!node.WasCompilerGenerated && _localRewriter.Instrument)
- {
+ if (GenerateInstrumentation)
translatedSwitch = _localRewriter._instrumenter.InstrumentSwitchStatement(node, translatedSwitch);
- }
return translatedSwitch;
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
index 279729631efe3..0decf1d0507f9 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_SwitchExpression.cs
@@ -29,13 +29,11 @@ public override BoundNode VisitConvertedSwitchExpression(BoundConvertedSwitchExp
private sealed class SwitchExpressionLocalRewriter : BaseSwitchLocalRewriter
{
private SwitchExpressionLocalRewriter(BoundConvertedSwitchExpression node, LocalRewriter localRewriter)
- : base(node.Syntax, localRewriter, node.SwitchArms.SelectAsArray(arm => arm.Syntax))
+ : base(node.Syntax, localRewriter, node.SwitchArms.SelectAsArray(arm => arm.Syntax),
+ generateInstrumentation: !node.WasCompilerGenerated && localRewriter.Instrument)
{
- GenerateSequencePoints = !node.WasCompilerGenerated && localRewriter.Instrument;
}
- protected override bool GenerateSequencePoints { get; }
-
public static BoundExpression Rewrite(LocalRewriter localRewriter, BoundConvertedSwitchExpression node)
{
var rewriter = new SwitchExpressionLocalRewriter(node, localRewriter);
@@ -48,7 +46,7 @@ private BoundExpression LowerSwitchExpression(BoundConvertedSwitchExpression nod
{
// When compiling for Debug (not Release), we produce the most detailed sequence points.
var produceDetailedSequencePoints =
- GenerateSequencePoints && _localRewriter._compilation.Options.OptimizationLevel != OptimizationLevel.Release;
+ GenerateInstrumentation && _localRewriter._compilation.Options.OptimizationLevel != OptimizationLevel.Release;
_factory.Syntax = node.Syntax;
var result = ArrayBuilder.GetInstance();
var outerVariables = ArrayBuilder.GetInstance();
@@ -91,11 +89,8 @@ private BoundExpression LowerSwitchExpression(BoundConvertedSwitchExpression nod
sectionBuilder.AddRange(switchSections[arm.Syntax]);
sectionBuilder.Add(_factory.Label(arm.Label));
var loweredValue = _localRewriter.VisitExpression(arm.Value);
- if (GenerateSequencePoints)
- {
- // Should go through this._localRewriter._instrumenter; see https://github.com/dotnet/roslyn/issues/42810
- loweredValue = new BoundSequencePointExpression(arm.Value.Syntax, loweredValue, loweredValue.Type);
- }
+ if (GenerateInstrumentation)
+ loweredValue = this._localRewriter._instrumenter.InstrumentSwitchExpressionArmExpression(arm.Value, loweredValue, _factory);
sectionBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), loweredValue));
sectionBuilder.Add(_factory.Goto(afterSwitchExpression));
diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
index 76d3eafd41add..bef0a1b29fab3 100644
--- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs
@@ -4076,7 +4076,7 @@ .locals init (int V_0, //a
IL_001b: ldloc.1
IL_001c: brtrue.s IL_0020
IL_001e: br.s IL_0027
- IL_0020: br.s IL_0022
+ ~IL_0020: br.s IL_0022
-IL_0022: ldloc.0
IL_0023: stloc.s V_4
IL_0025: br.s IL_002c
@@ -4152,7 +4152,7 @@ .locals init (int V_0, //i
IL_000e: unbox.any ""int""
IL_0013: stloc.0
~IL_0014: br.s IL_0016
- IL_0016: br.s IL_0018
+ ~IL_0016: br.s IL_0018
IL_0018: ldc.i4.1
IL_0019: brtrue.s IL_001c
-IL_001b: nop
@@ -4242,7 +4242,7 @@ .locals init (C.d__0 V_0)
-IL_001a: stfld ""int C.d__0.<>1__state""
IL_001f: ldloc.0
IL_0020: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder