Skip to content

Commit d5cd110

Browse files
authored
Avoid deep recursion in NullabilityRewriter.VisitBinaryPattern (#80711)
Related to #80569
1 parent 4b0d880 commit d5cd110

File tree

4 files changed

+42
-14
lines changed

4 files changed

+42
-14
lines changed

src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2529,7 +2529,7 @@
25292529
Left.InputType is the InputType
25302530
Right.InputType is the Left.NarrowedType
25312531
-->
2532-
<Node Name="BoundBinaryPattern" Base="BoundPattern" HasValidate="true">
2532+
<Node Name="BoundBinaryPattern" Base="BoundPattern" HasValidate="true" SkipInNullabilityRewriter="true">
25332533
<Field Name="Disjunction" Type="bool"/>
25342534
<Field Name="Left" Type="BoundPattern"/>
25352535
<Field Name="Right" Type="BoundPattern"/>

src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,41 @@ private BoundNode VisitBinaryOperatorBase(BoundBinaryOperatorBase binaryOperator
134134
return currentBinary!;
135135
}
136136

137+
public override BoundNode? VisitBinaryPattern(BoundBinaryPattern node)
138+
{
139+
// Use an explicit stack to avoid blowing the managed stack when visiting deeply-recursive
140+
// binary nodes
141+
var stack = ArrayBuilder<BoundBinaryPattern>.GetInstance();
142+
BoundBinaryPattern? currentBinary = node;
143+
144+
do
145+
{
146+
stack.Push(currentBinary);
147+
currentBinary = currentBinary.Left as BoundBinaryPattern;
148+
}
149+
while (currentBinary is not null);
150+
151+
Debug.Assert(stack.Count > 0);
152+
var leftChild = (BoundPattern)Visit(stack.Peek().Left);
153+
154+
do
155+
{
156+
currentBinary = stack.Pop();
157+
158+
TypeSymbol inputType = GetUpdatedSymbol(currentBinary, currentBinary.InputType);
159+
TypeSymbol narrowedType = GetUpdatedSymbol(currentBinary, currentBinary.NarrowedType);
160+
161+
var right = (BoundPattern)Visit(currentBinary.Right);
162+
163+
currentBinary = currentBinary.Update(currentBinary.Disjunction, leftChild, right, inputType, narrowedType);
164+
165+
leftChild = currentBinary;
166+
}
167+
while (stack.Count > 0);
168+
169+
return currentBinary;
170+
}
171+
137172
public override BoundNode? VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
138173
{
139174
ImmutableArray<MethodSymbol> originalUserDefinedOperatorsOpt = GetUpdatedArray(node, node.OriginalUserDefinedOperatorsOpt);

src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs

Lines changed: 0 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -892,11 +892,13 @@ public void ManyBinaryPatterns_01(string pattern, string expectedOutput)
892892
public void ManyBinaryPatterns_02()
893893
{
894894
const int numOfEnumMembers = 5_000;
895-
const int capacity = 97953;
895+
const int capacity = 97973;
896896

897897
var builder = new StringBuilder(capacity);
898898

899899
builder.Append("""
900+
#nullable enable
901+
900902
class ErrorFacts
901903
{
902904
static bool Test(E code)
@@ -942,7 +944,7 @@ enum E
942944
var source = builder.ToString();
943945
RunInThread(() =>
944946
{
945-
var comp = CreateCompilation(source, options: TestOptions.DebugDll.WithConcurrentBuild(false), parseOptions: TestOptions.RegularDefault.WithFeature("run-nullable-analysis", "never"));
947+
var comp = CreateCompilation(source, options: TestOptions.DebugDll.WithConcurrentBuild(false));
946948

947949
var tree = comp.SyntaxTrees.Single();
948950
var model = comp.GetSemanticModel(tree);
@@ -959,9 +961,9 @@ enum E
959961
Assert.NotNull(ControlFlowGraph.Create((IMethodBodyOperation)operation));
960962

961963
model.GetDiagnostics().Verify(
962-
// (5,21): warning CS8524: The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(E)5000' is not covered.
964+
// (7,21): warning CS8524: The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(E)5000' is not covered.
963965
// return code switch
964-
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveWithUnnamedEnumValue, "switch").WithArguments("(E)" + numOfEnumMembers).WithLocation(5, 21)
966+
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveWithUnnamedEnumValue, "switch").WithArguments("(E)" + numOfEnumMembers).WithLocation(7, 21)
965967
);
966968
});
967969
}

0 commit comments

Comments
 (0)