diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs
index a406f548b27d5..93a2c44efb109 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs
@@ -544,6 +544,14 @@ private void CheckDeclared(LocalSymbol local)
return null;
}
+ public override BoundNode? VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node)
+ {
+ AddAll(node.Locals);
+ base.VisitLoweredIsPatternExpression(node);
+ RemoveAll(node.Locals);
+ return null;
+ }
+
public override BoundNode? VisitSwitchStatement(BoundSwitchStatement node)
{
AddAll(node.InnerLocals);
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
index db0a64e70a2d5..e12773c35de5d 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
@@ -2265,6 +2265,7 @@
'is not Type t') will need to compensate for negated patterns. IsNegated is set if Pattern is the negated
form of the inner pattern represented by DecisionDag. -->
+
@@ -2273,6 +2274,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
index f6e06e022773e..60d6e55fb6390 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
@@ -315,6 +315,10 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
EmitRefValueOperator((BoundRefValueOperator)expression, used);
break;
+ case BoundKind.LoweredIsPatternExpression:
+ EmitLoweredIsPatternExpression((BoundLoweredIsPatternExpression)expression, used);
+ break;
+
case BoundKind.LoweredConditionalAccess:
EmitLoweredConditionalAccessExpression((BoundLoweredConditionalAccess)expression, used);
break;
@@ -352,6 +356,33 @@ private void EmitExpressionCore(BoundExpression expression, bool used)
}
}
+ private void EmitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node, bool used)
+ {
+ _builder.AssertStackEmpty();
+
+ DefineLocals(node.Syntax, node.Locals);
+ EmitStatements(node.Statements);
+
+ if (!used)
+ {
+ _builder.MarkLabel(node.WhenTrueLabel);
+ _builder.MarkLabel(node.WhenFalseLabel);
+ }
+ else
+ {
+ var doneLabel = new object();
+ _builder.MarkLabel(node.WhenTrueLabel);
+ _builder.EmitBoolConstant(true);
+ _builder.EmitBranch(ILOpCode.Br, doneLabel);
+ _builder.AdjustStack(-1);
+ _builder.MarkLabel(node.WhenFalseLabel);
+ _builder.EmitBoolConstant(false);
+ _builder.MarkLabel(doneLabel);
+ }
+
+ FreeLocals(node.Locals);
+ }
+
private void EmitThrowExpression(BoundThrowExpression node, bool used)
{
this.EmitThrow(node.Expression);
@@ -852,29 +883,39 @@ private void EmitSequenceExpression(BoundSequence sequence, bool used)
private void DefineLocals(BoundSequence sequence)
{
- if (sequence.Locals.IsEmpty)
+ DefineLocals(sequence.Syntax, sequence.Locals);
+ }
+
+ private void DefineLocals(SyntaxNode syntax, ImmutableArray locals)
+ {
+ if (locals.IsEmpty)
{
return;
}
_builder.OpenLocalScope();
- foreach (var local in sequence.Locals)
+ foreach (var local in locals)
{
- DefineLocal(local, sequence.Syntax);
+ DefineLocal(local, syntax);
}
}
private void FreeLocals(BoundSequence sequence)
{
- if (sequence.Locals.IsEmpty)
+ FreeLocals(sequence.Locals);
+ }
+
+ private void FreeLocals(ImmutableArray locals)
+ {
+ if (locals.IsEmpty)
{
return;
}
_builder.CloseLocalScope();
- foreach (var local in sequence.Locals)
+ foreach (var local in locals)
{
FreeLocal(local);
}
diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
index 36003e3bcf275..016249f8fae22 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
@@ -1447,6 +1447,16 @@ public override BoundNode VisitSwitchDispatch(BoundSwitchDispatch node)
return node.Update(boundExpression, node.Cases, node.DefaultLabel, node.LengthBasedStringSwitchDataOpt);
}
+ public override BoundNode VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node)
+ {
+ EnsureOnlyEvalStack();
+ DeclareLocals(node.Locals, stack: 0);
+ var result = base.VisitLoweredIsPatternExpression(node);
+ RecordBranch(node.WhenTrueLabel);
+ RecordBranch(node.WhenFalseLabel);
+ return result;
+ }
+
public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
{
var origStack = StackDepth();
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs
index 707c5bfd71a2b..62ea5b9cb9dca 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs
@@ -2973,6 +2973,11 @@ public override BoundNode VisitConditionalReceiver(BoundConditionalReceiver node
return null;
}
+ public override BoundNode VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node)
+ {
+ return null;
+ }
+
public override BoundNode VisitComplexConditionalReceiver(BoundComplexConditionalReceiver node)
{
var savedState = this.State.Clone();
diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
index efb758b5f0f8d..9f4bd55773cc9 100644
--- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
@@ -221,6 +221,7 @@ internal enum BoundKind : byte
InterpolatedStringArgumentPlaceholder,
StringInsert,
IsPatternExpression,
+ LoweredIsPatternExpression,
ConstantPattern,
DiscardPattern,
DeclarationPattern,
@@ -7626,7 +7627,7 @@ public BoundStringInsert Update(BoundExpression value, BoundExpression? alignmen
internal sealed partial class BoundIsPatternExpression : BoundExpression
{
- public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, BoundPattern pattern, bool isNegated, BoundDecisionDag reachabilityDecisionDag, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol? type, bool hasErrors = false)
+ public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, BoundPattern pattern, bool isNegated, BoundDecisionDag reachabilityDecisionDag, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.IsPatternExpression, syntax, type, hasErrors || expression.HasErrors() || pattern.HasErrors() || reachabilityDecisionDag.HasErrors())
{
@@ -7635,6 +7636,7 @@ public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, B
RoslynDebug.Assert(reachabilityDecisionDag is object, "Field 'reachabilityDecisionDag' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
RoslynDebug.Assert(whenTrueLabel is object, "Field 'whenTrueLabel' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
RoslynDebug.Assert(whenFalseLabel is object, "Field 'whenFalseLabel' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
+ RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
this.Expression = expression;
this.Pattern = pattern;
@@ -7644,6 +7646,7 @@ public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, B
this.WhenFalseLabel = whenFalseLabel;
}
+ public new TypeSymbol Type => base.Type!;
public BoundExpression Expression { get; }
public BoundPattern Pattern { get; }
public bool IsNegated { get; }
@@ -7654,7 +7657,7 @@ public BoundIsPatternExpression(SyntaxNode syntax, BoundExpression expression, B
[DebuggerStepThrough]
public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIsPatternExpression(this);
- public BoundIsPatternExpression Update(BoundExpression expression, BoundPattern pattern, bool isNegated, BoundDecisionDag reachabilityDecisionDag, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol? type)
+ public BoundIsPatternExpression Update(BoundExpression expression, BoundPattern pattern, bool isNegated, BoundDecisionDag reachabilityDecisionDag, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol type)
{
if (expression != this.Expression || pattern != this.Pattern || isNegated != this.IsNegated || reachabilityDecisionDag != this.ReachabilityDecisionDag || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(whenTrueLabel, this.WhenTrueLabel) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(whenFalseLabel, this.WhenFalseLabel) || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
{
@@ -7666,6 +7669,45 @@ public BoundIsPatternExpression Update(BoundExpression expression, BoundPattern
}
}
+ internal sealed partial class BoundLoweredIsPatternExpression : BoundExpression
+ {
+ public BoundLoweredIsPatternExpression(SyntaxNode syntax, ImmutableArray locals, ImmutableArray statements, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol type, bool hasErrors = false)
+ : base(BoundKind.LoweredIsPatternExpression, syntax, type, hasErrors || statements.HasErrors())
+ {
+
+ RoslynDebug.Assert(!locals.IsDefault, "Field 'locals' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
+ RoslynDebug.Assert(!statements.IsDefault, "Field 'statements' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)");
+ RoslynDebug.Assert(whenTrueLabel is object, "Field 'whenTrueLabel' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
+ RoslynDebug.Assert(whenFalseLabel is object, "Field 'whenFalseLabel' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
+ RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)");
+
+ this.Locals = locals;
+ this.Statements = statements;
+ this.WhenTrueLabel = whenTrueLabel;
+ this.WhenFalseLabel = whenFalseLabel;
+ }
+
+ public new TypeSymbol Type => base.Type!;
+ public ImmutableArray Locals { get; }
+ public ImmutableArray Statements { get; }
+ public LabelSymbol WhenTrueLabel { get; }
+ public LabelSymbol WhenFalseLabel { get; }
+
+ [DebuggerStepThrough]
+ public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitLoweredIsPatternExpression(this);
+
+ public BoundLoweredIsPatternExpression Update(ImmutableArray locals, ImmutableArray statements, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel, TypeSymbol type)
+ {
+ if (locals != this.Locals || statements != this.Statements || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(whenTrueLabel, this.WhenTrueLabel) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(whenFalseLabel, this.WhenFalseLabel) || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
+ {
+ var result = new BoundLoweredIsPatternExpression(this.Syntax, locals, statements, whenTrueLabel, whenFalseLabel, type, this.HasErrors);
+ result.CopyAttributes(this);
+ return result;
+ }
+ return this;
+ }
+ }
+
internal abstract partial class BoundPattern : BoundNode
{
protected BoundPattern(BoundKind kind, SyntaxNode syntax, TypeSymbol inputType, TypeSymbol narrowedType, bool hasErrors)
@@ -8913,6 +8955,8 @@ internal R VisitInternal(BoundNode node, A arg)
return VisitStringInsert((BoundStringInsert)node, arg);
case BoundKind.IsPatternExpression:
return VisitIsPatternExpression((BoundIsPatternExpression)node, arg);
+ case BoundKind.LoweredIsPatternExpression:
+ return VisitLoweredIsPatternExpression((BoundLoweredIsPatternExpression)node, arg);
case BoundKind.ConstantPattern:
return VisitConstantPattern((BoundConstantPattern)node, arg);
case BoundKind.DiscardPattern:
@@ -9168,6 +9212,7 @@ internal abstract partial class BoundTreeVisitor
public virtual R VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitStringInsert(BoundStringInsert node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitIsPatternExpression(BoundIsPatternExpression node, A arg) => this.DefaultVisit(node, arg);
+ public virtual R VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitConstantPattern(BoundConstantPattern node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitDiscardPattern(BoundDiscardPattern node, A arg) => this.DefaultVisit(node, arg);
public virtual R VisitDeclarationPattern(BoundDeclarationPattern node, A arg) => this.DefaultVisit(node, arg);
@@ -9396,6 +9441,7 @@ internal abstract partial class BoundTreeVisitor
public virtual BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) => this.DefaultVisit(node);
public virtual BoundNode? VisitStringInsert(BoundStringInsert node) => this.DefaultVisit(node);
public virtual BoundNode? VisitIsPatternExpression(BoundIsPatternExpression node) => this.DefaultVisit(node);
+ public virtual BoundNode? VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node) => this.DefaultVisit(node);
public virtual BoundNode? VisitConstantPattern(BoundConstantPattern node) => this.DefaultVisit(node);
public virtual BoundNode? VisitDiscardPattern(BoundDiscardPattern node) => this.DefaultVisit(node);
public virtual BoundNode? VisitDeclarationPattern(BoundDeclarationPattern node) => this.DefaultVisit(node);
@@ -10299,6 +10345,11 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor
this.Visit(node.Pattern);
return null;
}
+ public override BoundNode? VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node)
+ {
+ this.VisitList(node.Statements);
+ return null;
+ }
public override BoundNode? VisitConstantPattern(BoundConstantPattern node)
{
this.Visit(node.Value);
@@ -11616,6 +11667,12 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor
TypeSymbol? type = this.VisitType(node.Type);
return node.Update(expression, pattern, node.IsNegated, reachabilityDecisionDag, node.WhenTrueLabel, node.WhenFalseLabel, type);
}
+ public override BoundNode? VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node)
+ {
+ ImmutableArray statements = this.VisitList(node.Statements);
+ TypeSymbol? type = this.VisitType(node.Type);
+ return node.Update(node.Locals, statements, node.WhenTrueLabel, node.WhenFalseLabel, type);
+ }
public override BoundNode? VisitConstantPattern(BoundConstantPattern node)
{
BoundExpression value = (BoundExpression)this.Visit(node.Value);
@@ -14160,7 +14217,7 @@ public NullabilityRewriter(ImmutableDictionary locals = GetUpdatedArray(node, node.Locals);
+ ImmutableArray statements = this.VisitList(node.Statements);
+ BoundLoweredIsPatternExpression updatedNode;
+
+ if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType))
+ {
+ updatedNode = node.Update(locals, statements, node.WhenTrueLabel, node.WhenFalseLabel, infoAndType.Type!);
+ updatedNode.TopLevelNullability = infoAndType.Info;
+ }
+ else
+ {
+ updatedNode = node.Update(locals, statements, node.WhenTrueLabel, node.WhenFalseLabel, node.Type);
+ }
+ return updatedNode;
+ }
+
public override BoundNode? VisitConstantPattern(BoundConstantPattern node)
{
TypeSymbol inputType = GetUpdatedSymbol(node, node.InputType);
@@ -16284,6 +16359,17 @@ private BoundTreeDumperNodeProducer()
new TreeDumperNode("hasErrors", node.HasErrors, null)
}
);
+ public override TreeDumperNode VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node, object? arg) => new TreeDumperNode("loweredIsPatternExpression", null, new TreeDumperNode[]
+ {
+ new TreeDumperNode("locals", node.Locals, null),
+ new TreeDumperNode("statements", null, from x in node.Statements select Visit(x, null)),
+ new TreeDumperNode("whenTrueLabel", node.WhenTrueLabel, null),
+ new TreeDumperNode("whenFalseLabel", node.WhenFalseLabel, null),
+ new TreeDumperNode("type", node.Type, null),
+ new TreeDumperNode("isSuppressed", node.IsSuppressed, null),
+ new TreeDumperNode("hasErrors", node.HasErrors, null)
+ }
+ );
public override TreeDumperNode VisitConstantPattern(BoundConstantPattern node, object? arg) => new TreeDumperNode("constantPattern", null, new TreeDumperNode[]
{
new TreeDumperNode("value", null, new TreeDumperNode[] { Visit(node.Value, null) }),
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs
index 753a242ab44fc..b20701b2e9f5d 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IsPatternOperator.cs
@@ -108,6 +108,8 @@ public IsPatternExpressionGeneralLocalRewriter(
internal BoundExpression LowerGeneralIsPattern(BoundIsPatternExpression node, BoundDecisionDag decisionDag)
{
+ Debug.Assert(node.Type is { SpecialType: SpecialType.System_Boolean });
+
_factory.Syntax = node.Syntax;
var resultBuilder = ArrayBuilder.GetInstance();
var inputExpression = _localRewriter.VisitExpression(node.Expression);
@@ -115,22 +117,11 @@ internal BoundExpression LowerGeneralIsPattern(BoundIsPatternExpression node, Bo
// lower the decision dag.
ImmutableArray loweredDag = LowerDecisionDagCore(decisionDag);
- resultBuilder.Add(_factory.Block(loweredDag));
- Debug.Assert(node.Type is { SpecialType: SpecialType.System_Boolean });
- LocalSymbol resultTemp = _factory.SynthesizedLocal(node.Type, node.Syntax, kind: SynthesizedLocalKind.LoweringTemp);
- LabelSymbol afterIsPatternExpression = _factory.GenerateLabel("afterIsPatternExpression");
- LabelSymbol trueLabel = node.WhenTrueLabel;
- LabelSymbol falseLabel = node.WhenFalseLabel;
- if (_statements.Count != 0)
- resultBuilder.Add(_factory.Block(_statements.ToArray()));
- resultBuilder.Add(_factory.Label(trueLabel));
- resultBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), _factory.Literal(true)));
- resultBuilder.Add(_factory.Goto(afterIsPatternExpression));
- resultBuilder.Add(_factory.Label(falseLabel));
- resultBuilder.Add(_factory.Assignment(_factory.Local(resultTemp), _factory.Literal(false)));
- resultBuilder.Add(_factory.Label(afterIsPatternExpression));
+ resultBuilder.AddRange(loweredDag);
+ resultBuilder.AddRange(_statements);
_localRewriter._needsSpilling = true;
- return _factory.SpillSequence(_tempAllocator.AllTemps().Add(resultTemp), resultBuilder.ToImmutableAndFree(), _factory.Local(resultTemp));
+ return new BoundLoweredIsPatternExpression(node.Syntax, _tempAllocator.AllTemps(),
+ resultBuilder.ToImmutableAndFree(), node.WhenTrueLabel, node.WhenFalseLabel, node.Type);
}
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs
index 631b689c990a8..f106c64fc4beb 100644
--- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs
@@ -45,7 +45,7 @@ private sealed class BoundSpillSequenceBuilder : BoundExpression
public BoundSpillSequenceBuilder(SyntaxNode syntax, BoundExpression value = null)
: base(SpillSequenceBuilderKind, syntax, value?.Type)
{
- Debug.Assert(value?.Kind != SpillSequenceBuilderKind);
+ Debug.Assert(value is null || !IsSpillSequenceProducer(value));
this.Value = value;
}
@@ -122,12 +122,7 @@ private static void IncludeAndFree(ref ArrayBuilder left, ref ArrayBuilder
public void AddLocal(LocalSymbol local)
{
- if (_locals == null)
- {
- _locals = ArrayBuilder.GetInstance();
- }
-
- _locals.Add(local);
+ (_locals ??= ArrayBuilder.GetInstance()).Add(local);
}
public void AddLocals(ImmutableArray locals)
@@ -140,12 +135,7 @@ public void AddLocals(ImmutableArray locals)
public void AddStatement(BoundStatement statement)
{
- if (_statements == null)
- {
- _statements = ArrayBuilder.GetInstance();
- }
-
- _statements.Add(statement);
+ (_statements ??= ArrayBuilder.GetInstance()).Add(statement);
}
public void AddStatements(ImmutableArray statements)
@@ -242,25 +232,43 @@ internal static BoundStatement Rewrite(BoundStatement body, MethodSymbol method,
return (BoundStatement)result;
}
- private BoundExpression VisitExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression)
+ private BoundExpression VisitTopLevelExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression)
+ {
+ Debug.Assert(builder is null);
+ return VisitExpression(ref builder, expression, topLevel: true);
+ }
+
+ private BoundExpression VisitExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression, bool topLevel = false)
{
var e = (BoundExpression)this.Visit(expression);
- if (e == null || e.Kind != SpillSequenceBuilderKind)
+ switch (e)
{
- return e;
- }
+ case BoundSpillSequenceBuilder newBuilder:
+ if (builder == null)
+ {
+ builder = newBuilder.Update(null);
+ }
+ else
+ {
+ builder.Include(newBuilder);
+ }
- var newBuilder = (BoundSpillSequenceBuilder)e;
- if (builder == null)
- {
- builder = newBuilder.Update(null);
- }
- else
- {
- builder.Include(newBuilder);
- }
+ return newBuilder.Value;
+
+ case BoundLoweredIsPatternExpression loweredIs:
+ if (!topLevel)
+ {
+ builder ??= new BoundSpillSequenceBuilder(expression.Syntax);
- return newBuilder.Value;
+ return Spill(builder, loweredIs);
+ }
+
+ return loweredIs;
+
+ default:
+ Debug.Assert(e is null || !IsSpillSequenceProducer(e));
+ return e;
+ }
}
private static BoundExpression UpdateExpression(BoundSpillSequenceBuilder builder, BoundExpression expression)
@@ -429,6 +437,24 @@ private BoundExpression Spill(
// later, if needed
return expression;
+ case BoundKind.LoweredIsPatternExpression:
+ var loweredIs = (BoundLoweredIsPatternExpression)expression;
+
+ var doneLabel = _F.GenerateLabel("afterIsExpression");
+ var resultTemp = _F.SynthesizedLocal(loweredIs.Type, syntax: _F.Syntax);
+
+ builder.AddLocals(loweredIs.Locals);
+ builder.AddLocal(resultTemp);
+ builder.AddStatements(loweredIs.Statements);
+ builder.AddStatement(_F.Label(loweredIs.WhenTrueLabel));
+ builder.AddStatement(_F.Assignment(_F.Local(resultTemp), _F.Literal(true)));
+ builder.AddStatement(_F.Goto(doneLabel));
+ builder.AddStatement(_F.Label(loweredIs.WhenFalseLabel));
+ builder.AddStatement(_F.Assignment(_F.Local(resultTemp), _F.Literal(false)));
+ builder.AddStatement(_F.Label(doneLabel));
+
+ return _F.Local(resultTemp);
+
default:
if (expression.Type.IsVoidType() || sideEffectsOnly)
{
@@ -537,7 +563,7 @@ private ImmutableArray VisitExpressionList(
lastSpill = -1;
for (int i = newList.Length - 1; i >= 0; i--)
{
- if (newList[i].Kind == SpillSequenceBuilderKind)
+ if (IsSpillSequenceProducer(newList[i]))
{
lastSpill = i;
break;
@@ -550,10 +576,8 @@ private ImmutableArray VisitExpressionList(
return newList;
}
- if (builder == null)
- {
- builder = new BoundSpillSequenceBuilder(lastSpill < newList.Length ? (newList[lastSpill] as BoundSpillSequenceBuilder)?.Syntax : null);
- }
+ var lastSpillNode = lastSpill < newList.Length ? newList[lastSpill] : null;
+ builder ??= new BoundSpillSequenceBuilder(lastSpillNode?.Syntax);
var result = ArrayBuilder.GetInstance(newList.Length);
@@ -574,9 +598,18 @@ private ImmutableArray VisitExpressionList(
// the value of the last spill and everything that follows is not spilled
if (lastSpill < newList.Length)
{
- var lastSpillNode = (BoundSpillSequenceBuilder)newList[lastSpill];
- builder.Include(lastSpillNode);
- result.Add(lastSpillNode.Value);
+ switch (lastSpillNode)
+ {
+ case BoundSpillSequenceBuilder newBuilder:
+ builder.Include(newBuilder);
+ result.Add(newBuilder.Value);
+ break;
+ case BoundLoweredIsPatternExpression loweredIs:
+ result.Add(Spill(builder, loweredIs));
+ break;
+ default:
+ throw ExceptionUtilities.Unreachable();
+ }
for (int i = lastSpill + 1; i < newList.Length; i++)
{
@@ -587,26 +620,31 @@ private ImmutableArray VisitExpressionList(
return result.ToImmutableAndFree();
}
+ private static bool IsSpillSequenceProducer(BoundExpression expression)
+ {
+ return expression.Kind is SpillSequenceBuilderKind or BoundKind.LoweredIsPatternExpression;
+ }
+
#region Statement Visitors
public override BoundNode VisitSwitchDispatch(BoundSwitchDispatch node)
{
BoundSpillSequenceBuilder builder = null;
- var expression = VisitExpression(ref builder, node.Expression);
+ var expression = VisitTopLevelExpression(ref builder, node.Expression);
return UpdateStatement(builder, node.Update(expression, node.Cases, node.DefaultLabel, node.LengthBasedStringSwitchDataOpt));
}
public override BoundNode VisitThrowStatement(BoundThrowStatement node)
{
BoundSpillSequenceBuilder builder = null;
- BoundExpression expression = VisitExpression(ref builder, node.ExpressionOpt);
+ BoundExpression expression = VisitTopLevelExpression(ref builder, node.ExpressionOpt);
return UpdateStatement(builder, node.Update(expression));
}
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
{
BoundSpillSequenceBuilder builder = null;
- BoundExpression expr = VisitExpression(ref builder, node.Expression);
+ BoundExpression expr = VisitTopLevelExpression(ref builder, node.Expression);
Debug.Assert(expr != null);
Debug.Assert(builder == null || builder.Value == null);
return UpdateStatement(builder, node.Update(expr));
@@ -615,21 +653,21 @@ public override BoundNode VisitExpressionStatement(BoundExpressionStatement node
public override BoundNode VisitConditionalGoto(BoundConditionalGoto node)
{
BoundSpillSequenceBuilder builder = null;
- var condition = VisitExpression(ref builder, node.Condition);
+ var condition = VisitTopLevelExpression(ref builder, node.Condition);
return UpdateStatement(builder, node.Update(condition, node.JumpIfTrue, node.Label));
}
public override BoundNode VisitReturnStatement(BoundReturnStatement node)
{
BoundSpillSequenceBuilder builder = null;
- var expression = VisitExpression(ref builder, node.ExpressionOpt);
+ var expression = VisitTopLevelExpression(ref builder, node.ExpressionOpt);
return UpdateStatement(builder, node.Update(node.RefKind, expression, @checked: node.Checked));
}
public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node)
{
BoundSpillSequenceBuilder builder = null;
- var expression = VisitExpression(ref builder, node.Expression);
+ var expression = VisitTopLevelExpression(ref builder, node.Expression);
return UpdateStatement(builder, node.Update(expression));
}
@@ -666,6 +704,12 @@ public override BoundNode DefaultVisit(BoundNode node)
#region Expression Visitors
+ public override BoundNode VisitLoweredIsPatternExpression(BoundLoweredIsPatternExpression node)
+ {
+ // the spilling will occur in the enclosing node.
+ return node;
+ }
+
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
{
// An await expression has already been wrapped in a BoundSpillSequence if not at the top level, so
@@ -713,10 +757,7 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node)
if (indicesBuilder != null)
{
// spill the array if there were await expressions in the indices
- if (builder == null)
- {
- builder = new BoundSpillSequenceBuilder(indicesBuilder.Syntax);
- }
+ builder ??= new BoundSpillSequenceBuilder(indicesBuilder.Syntax);
expression = Spill(builder, expression);
}
@@ -1046,9 +1087,9 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node
return UpdateExpression(conditionBuilder, node.Update(node.IsRef, condition, consequence, alternative, node.ConstantValueOpt, node.NaturalTypeOpt, node.WasTargetTyped, node.Type));
}
- if (conditionBuilder == null) conditionBuilder = new BoundSpillSequenceBuilder((consequenceBuilder ?? alternativeBuilder).Syntax);
- if (consequenceBuilder == null) consequenceBuilder = new BoundSpillSequenceBuilder(alternativeBuilder.Syntax);
- if (alternativeBuilder == null) alternativeBuilder = new BoundSpillSequenceBuilder(consequenceBuilder.Syntax);
+ conditionBuilder ??= new BoundSpillSequenceBuilder((consequenceBuilder ?? alternativeBuilder).Syntax);
+ consequenceBuilder ??= new BoundSpillSequenceBuilder(alternativeBuilder.Syntax);
+ alternativeBuilder ??= new BoundSpillSequenceBuilder(consequenceBuilder.Syntax);
if (node.Type.IsVoidType())
{
@@ -1180,9 +1221,9 @@ public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalA
return UpdateExpression(receiverBuilder, node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.ForceCopyOfNullableValueType, node.Type));
}
- if (receiverBuilder == null) receiverBuilder = new BoundSpillSequenceBuilder((whenNotNullBuilder ?? whenNullBuilder).Syntax);
- if (whenNotNullBuilder == null) whenNotNullBuilder = new BoundSpillSequenceBuilder(whenNullBuilder.Syntax);
- if (whenNullBuilder == null) whenNullBuilder = new BoundSpillSequenceBuilder(whenNotNullBuilder.Syntax);
+ receiverBuilder ??= new BoundSpillSequenceBuilder((whenNotNullBuilder ?? whenNullBuilder).Syntax);
+ whenNotNullBuilder ??= new BoundSpillSequenceBuilder(whenNullBuilder.Syntax);
+ whenNullBuilder ??= new BoundSpillSequenceBuilder(whenNotNullBuilder.Syntax);
BoundExpression condition;
if (receiver.Type.IsReferenceType || receiver.Type.IsValueType || receiverRefKind == RefKind.None)
@@ -1367,10 +1408,7 @@ public override BoundNode VisitSequence(BoundSequence node)
return node.Update(node.Locals, sideEffects, value, node.Type);
}
- if (builder == null)
- {
- builder = new BoundSpillSequenceBuilder(valueBuilder.Syntax);
- }
+ builder ??= new BoundSpillSequenceBuilder(valueBuilder.Syntax);
PromoteAndAddLocals(builder, node.Locals);
builder.AddExpressions(sideEffects);
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
index caad6cb2b227c..2167d0711ef17 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
@@ -5519,23 +5519,19 @@ public static void Main()
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
compVerifier.VerifyIL("C.M1", """
{
- // Code size 26 (0x1a)
+ // Code size 23 (0x17)
.maxstack 1
- .locals init (bool V_0)
IL_0000: ldarg.0
IL_0001: isinst "int"
IL_0006: brtrue.s IL_0012
IL_0008: ldarg.0
IL_0009: isinst "long"
IL_000e: brtrue.s IL_0012
- IL_0010: br.s IL_0016
+ IL_0010: br.s IL_0015
IL_0012: ldc.i4.1
- IL_0013: stloc.0
- IL_0014: br.s IL_0018
- IL_0016: ldc.i4.0
- IL_0017: stloc.0
- IL_0018: ldloc.0
- IL_0019: ret
+ IL_0013: br.s IL_0016
+ IL_0015: ldc.i4.0
+ IL_0016: ret
}
""");
compVerifier.VerifyIL("C.M2", """
@@ -5560,22 +5556,18 @@ .maxstack 2
compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
compVerifier.VerifyIL("C.M1", """
{
- // Code size 24 (0x18)
+ // Code size 20 (0x14)
.maxstack 1
- .locals init (bool V_0)
IL_0000: ldarg.0
IL_0001: isinst "int"
IL_0006: brtrue.s IL_0010
IL_0008: ldarg.0
IL_0009: isinst "long"
- IL_000e: brfalse.s IL_0014
+ IL_000e: brfalse.s IL_0012
IL_0010: ldc.i4.1
- IL_0011: stloc.0
- IL_0012: br.s IL_0016
- IL_0014: ldc.i4.0
- IL_0015: stloc.0
- IL_0016: ldloc.0
- IL_0017: ret
+ IL_0011: ret
+ IL_0012: ldc.i4.0
+ IL_0013: ret
}
""");
compVerifier.VerifyIL("C.M2", @"
@@ -5725,41 +5717,37 @@ public static void Main()
compilation.VerifyDiagnostics();
var expectedOutput = @"TrueFalseTrueFalse";
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
- compVerifier.VerifyIL("C.M1", @"
- {
- // Code size 47 (0x2f)
- .maxstack 2
- .locals init (char V_0,
- bool V_1)
- IL_0000: ldarg.0
- IL_0001: isinst ""char""
- IL_0006: brfalse.s IL_002b
- IL_0008: ldarg.0
- IL_0009: unbox.any ""char""
- IL_000e: stloc.0
- IL_000f: ldloc.0
- IL_0010: ldc.i4.s 97
- IL_0012: blt.s IL_001b
- IL_0014: ldloc.0
- IL_0015: ldc.i4.s 122
- IL_0017: ble.s IL_0027
- IL_0019: br.s IL_002b
- IL_001b: ldloc.0
- IL_001c: ldc.i4.s 65
- IL_001e: blt.s IL_002b
- IL_0020: ldloc.0
- IL_0021: ldc.i4.s 90
- IL_0023: ble.s IL_0027
- IL_0025: br.s IL_002b
- IL_0027: ldc.i4.1
- IL_0028: stloc.1
- IL_0029: br.s IL_002d
- IL_002b: ldc.i4.0
- IL_002c: stloc.1
- IL_002d: ldloc.1
- IL_002e: ret
- }
-");
+ compVerifier.VerifyIL("C.M1", """
+{
+ // Code size 44 (0x2c)
+ .maxstack 2
+ .locals init (char V_0)
+ IL_0000: ldarg.0
+ IL_0001: isinst "char"
+ IL_0006: brfalse.s IL_002a
+ IL_0008: ldarg.0
+ IL_0009: unbox.any "char"
+ IL_000e: stloc.0
+ IL_000f: ldloc.0
+ IL_0010: ldc.i4.s 97
+ IL_0012: blt.s IL_001b
+ IL_0014: ldloc.0
+ IL_0015: ldc.i4.s 122
+ IL_0017: ble.s IL_0027
+ IL_0019: br.s IL_002a
+ IL_001b: ldloc.0
+ IL_001c: ldc.i4.s 65
+ IL_001e: blt.s IL_002a
+ IL_0020: ldloc.0
+ IL_0021: ldc.i4.s 90
+ IL_0023: ble.s IL_0027
+ IL_0025: br.s IL_002a
+ IL_0027: ldc.i4.1
+ IL_0028: br.s IL_002b
+ IL_002a: ldc.i4.0
+ IL_002b: ret
+}
+""");
compVerifier.VerifyIL("C.M2", @"
{
// Code size 48 (0x30)
@@ -5798,40 +5786,36 @@ .locals init (char V_0) //c
compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithPatternCombinators);
compilation.VerifyDiagnostics();
compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
- compVerifier.VerifyIL("C.M1", @"
- {
- // Code size 45 (0x2d)
- .maxstack 2
- .locals init (char V_0,
- bool V_1)
- IL_0000: ldarg.0
- IL_0001: isinst ""char""
- IL_0006: brfalse.s IL_0029
- IL_0008: ldarg.0
- IL_0009: unbox.any ""char""
- IL_000e: stloc.0
- IL_000f: ldloc.0
- IL_0010: ldc.i4.s 97
- IL_0012: blt.s IL_001b
- IL_0014: ldloc.0
- IL_0015: ldc.i4.s 122
- IL_0017: ble.s IL_0025
- IL_0019: br.s IL_0029
- IL_001b: ldloc.0
- IL_001c: ldc.i4.s 65
- IL_001e: blt.s IL_0029
- IL_0020: ldloc.0
- IL_0021: ldc.i4.s 90
- IL_0023: bgt.s IL_0029
- IL_0025: ldc.i4.1
- IL_0026: stloc.1
- IL_0027: br.s IL_002b
- IL_0029: ldc.i4.0
- IL_002a: stloc.1
- IL_002b: ldloc.1
- IL_002c: ret
- }
-");
+ compVerifier.VerifyIL("C.M1", """
+{
+ // Code size 41 (0x29)
+ .maxstack 2
+ .locals init (char V_0)
+ IL_0000: ldarg.0
+ IL_0001: isinst "char"
+ IL_0006: brfalse.s IL_0027
+ IL_0008: ldarg.0
+ IL_0009: unbox.any "char"
+ IL_000e: stloc.0
+ IL_000f: ldloc.0
+ IL_0010: ldc.i4.s 97
+ IL_0012: blt.s IL_001b
+ IL_0014: ldloc.0
+ IL_0015: ldc.i4.s 122
+ IL_0017: ble.s IL_0025
+ IL_0019: br.s IL_0027
+ IL_001b: ldloc.0
+ IL_001c: ldc.i4.s 65
+ IL_001e: blt.s IL_0027
+ IL_0020: ldloc.0
+ IL_0021: ldc.i4.s 90
+ IL_0023: bgt.s IL_0027
+ IL_0025: ldc.i4.1
+ IL_0026: ret
+ IL_0027: ldc.i4.0
+ IL_0028: ret
+}
+""");
compVerifier.VerifyIL("C.M2", @"
{
// Code size 45 (0x2d)
@@ -6118,33 +6102,29 @@ .locals init (bool V_0,
compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
compVerifier.VerifyIL("C.M1", @"
{
- // Code size 35 (0x23)
+ // Code size 32 (0x20)
.maxstack 2
- .locals init (bool V_0)
IL_0000: ldarg.0
IL_0001: ldc.i4.s 97
IL_0003: blt.s IL_000c
IL_0005: ldarg.0
IL_0006: ldc.i4.s 122
IL_0008: ble.s IL_0016
- IL_000a: br.s IL_001a
+ IL_000a: br.s IL_0019
IL_000c: ldarg.0
IL_000d: ldc.i4.s 65
- IL_000f: blt.s IL_001a
+ IL_000f: blt.s IL_0019
IL_0011: ldarg.0
IL_0012: ldc.i4.s 90
- IL_0014: bgt.s IL_001a
+ IL_0014: bgt.s IL_0019
IL_0016: ldc.i4.1
- IL_0017: stloc.0
- IL_0018: br.s IL_001c
- IL_001a: ldc.i4.0
- IL_001b: stloc.0
- IL_001c: ldloc.0
- IL_001d: brfalse.s IL_0021
- IL_001f: ldc.i4.1
- IL_0020: ret
- IL_0021: ldc.i4.0
- IL_0022: ret
+ IL_0017: br.s IL_001a
+ IL_0019: ldc.i4.0
+ IL_001a: brfalse.s IL_001e
+ IL_001c: ldc.i4.1
+ IL_001d: ret
+ IL_001e: ldc.i4.0
+ IL_001f: ret
}
");
compVerifier.VerifyIL("C.M2", @"
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
index 9e114ce1aec4a..7d66a3a0286e5 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs
@@ -7125,7 +7125,7 @@ .maxstack 2
IL_0760: ret
}";
compVerifier.VerifyIL("ConsoleApplication24.Program.IsWarning", codeForSwitchStatement);
- compVerifier.VerifyIL("ConsoleApplication24.Program.IsWarning_IsExpression", codeForExpression);
+ compVerifier.VerifyIL("ConsoleApplication24.Program.IsWarning_IsExpression", codeForSwitchStatement);
compVerifier.VerifyIL("ConsoleApplication24.Program.IsWarning_SwitchExpression", codeForExpression);
}
diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests5.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests5.cs
index 0120dc6e33d29..14bfe93d9dc4a 100644
--- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests5.cs
+++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests5.cs
@@ -3178,21 +3178,17 @@ public static bool Test(int a)
var compilation = CompileAndVerify(source, expectedOutput: "True");
compilation.VerifyIL("C.Test", """
{
- // Code size 14 (0xe)
+ // Code size 10 (0xa)
.maxstack 2
- .locals init (bool V_0)
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: sub
IL_0003: ldc.i4.7
- IL_0004: bgt.un.s IL_000a
+ IL_0004: bgt.un.s IL_0008
IL_0006: ldc.i4.1
- IL_0007: stloc.0
- IL_0008: br.s IL_000c
- IL_000a: ldc.i4.0
- IL_000b: stloc.0
- IL_000c: ldloc.0
- IL_000d: ret
+ IL_0007: ret
+ IL_0008: ldc.i4.0
+ IL_0009: ret
}
""");
}
@@ -3227,9 +3223,8 @@ public static bool Test(int a, object b)
var compilation = CompileAndVerify(source, expectedOutput: "True");
compilation.VerifyIL("C.Test", """
{
- // Code size 72 (0x48)
+ // Code size 68 (0x44)
.maxstack 2
- .locals init (bool V_0)
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: sub
@@ -3238,29 +3233,26 @@ .locals init (bool V_0)
IL_0024,
IL_002e,
IL_0038)
- IL_0018: br.s IL_0044
+ IL_0018: br.s IL_0042
IL_001a: ldarg.1
IL_001b: isinst "int"
IL_0020: brtrue.s IL_0040
- IL_0022: br.s IL_0044
+ IL_0022: br.s IL_0042
IL_0024: ldarg.1
IL_0025: isinst "bool"
IL_002a: brtrue.s IL_0040
- IL_002c: br.s IL_0044
+ IL_002c: br.s IL_0042
IL_002e: ldarg.1
IL_002f: isinst "double"
IL_0034: brtrue.s IL_0040
- IL_0036: br.s IL_0044
+ IL_0036: br.s IL_0042
IL_0038: ldarg.1
IL_0039: isinst "long"
- IL_003e: brfalse.s IL_0044
+ IL_003e: brfalse.s IL_0042
IL_0040: ldc.i4.1
- IL_0041: stloc.0
- IL_0042: br.s IL_0046
- IL_0044: ldc.i4.0
- IL_0045: stloc.0
- IL_0046: ldloc.0
- IL_0047: ret
+ IL_0041: ret
+ IL_0042: ldc.i4.0
+ IL_0043: ret
}
""");
}
@@ -3294,24 +3286,23 @@ public static bool Test(string a)
var compilation = CompileAndVerify(source, expectedOutput: "True");
compilation.VerifyIL("C.Test", """
{
- // Code size 73 (0x49)
+ // Code size 69 (0x45)
.maxstack 2
- .locals init (bool V_0,
- int V_1,
- char V_2)
+ .locals init (int V_0,
+ char V_1)
IL_0000: ldarg.0
- IL_0001: brfalse.s IL_0045
+ IL_0001: brfalse.s IL_0043
IL_0003: ldarg.0
IL_0004: call "int string.Length.get"
- IL_0009: stloc.1
- IL_000a: ldloc.1
+ IL_0009: stloc.0
+ IL_000a: ldloc.0
IL_000b: ldc.i4.1
- IL_000c: bne.un.s IL_0045
+ IL_000c: bne.un.s IL_0043
IL_000e: ldarg.0
IL_000f: ldc.i4.0
IL_0010: call "char string.this[int].get"
- IL_0015: stloc.2
- IL_0016: ldloc.2
+ IL_0015: stloc.1
+ IL_0016: ldloc.1
IL_0017: ldc.i4.s 49
IL_0019: sub
IL_001a: switch (
@@ -3323,14 +3314,11 @@ .locals init (bool V_0,
IL_0041,
IL_0041,
IL_0041)
- IL_003f: br.s IL_0045
+ IL_003f: br.s IL_0043
IL_0041: ldc.i4.1
- IL_0042: stloc.0
- IL_0043: br.s IL_0047
- IL_0045: ldc.i4.0
- IL_0046: stloc.0
- IL_0047: ldloc.0
- IL_0048: ret
+ IL_0042: ret
+ IL_0043: ldc.i4.0
+ IL_0044: ret
}
""");
}
@@ -3356,5 +3344,27 @@ bool M0({type} x0)
Diagnostic(ErrorCode.ERR_ConstantExpected, expression).WithLocation(6, 22)
);
}
+
+ [Fact]
+ public void BootstrapFail()
+ {
+ var source = """
+using System;
+using System.Diagnostics;
+
+class C
+{
+ char PeekNextChar() => ' ';
+ void Assert(bool b) {}
+ object ParseParameterList()
+ {
+ Assert(PeekNextChar() is '(' or '[');
+ return null;
+ }
+}
+""";
+ var verifier = CompileAndVerify(source, options: TestOptions.ReleaseDll);
+
+ }
}
}