Skip to content

Commit 140faba

Browse files
committed
Fix #2903: Reduce the depth of trees generated in PatternMatcher
* Extract all match arguments before checking conditions on them like scalac does. This avoids an extra nested block for each match variable. * Merge conditions of nested `if` expressions if their `else` branch is the same. This optimization combined with the previous removes most of the nested `if`s created to check the matched args.
1 parent 52279cb commit 140faba

File tree

2 files changed

+122
-10
lines changed

2 files changed

+122
-10
lines changed

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,19 @@ object PatternMatcher {
212212
private def patternPlan(scrutinee: Symbol, tree: Tree, onSuccess: Plan, onFailure: Plan): Plan = {
213213

214214
/** Plan for matching `selectors` against argument patterns `args` */
215-
def matchArgsPlan(selectors: List[Tree], args: List[Tree], onSuccess: Plan): Plan =
216-
args match {
217-
case arg :: args1 =>
218-
val selector :: selectors1 = selectors
219-
letAbstract(selector)(
220-
patternPlan(_, arg, matchArgsPlan(selectors1, args1, onSuccess), onFailure))
221-
case Nil => onSuccess
222-
}
215+
def matchArgsPlan(selectors: List[Tree], args: List[Tree], onSuccess: Plan): Plan = {
216+
def matchArgsSelectorsPlan(selectors: List[Tree], syms: List[Symbol]): Plan =
217+
selectors match {
218+
case selector :: selectors1 => letAbstract(selector)(sym => matchArgsSelectorsPlan(selectors1, sym :: syms))
219+
case Nil => matchArgsPatternPlan(args, syms.reverse)
220+
}
221+
def matchArgsPatternPlan(args: List[Tree], syms: List[Symbol]): Plan =
222+
((args, syms): @unchecked) match { // both lists should always have the same size
223+
case (arg :: args1, sym :: syms1) => patternPlan(sym, arg, matchArgsPatternPlan(args1, syms1), onFailure)
224+
case (Nil, Nil) => onSuccess
225+
}
226+
matchArgsSelectorsPlan(selectors, Nil)
227+
}
223228

224229
/** Plan for matching the sequence in `seqSym` against sequence elements `args`.
225230
* If `exact` is true, the sequence is not permitted to have any elements following `args`.
@@ -837,8 +842,28 @@ object PatternMatcher {
837842
val switchCases = collectSwitchCases(plan)
838843
if (switchCases.lengthCompare(4) >= 0) // at least 3 cases + default
839844
Match(plan.scrutinee, emitSwitchCases(switchCases))
840-
else
841-
If(emitCondition(plan).withPos(plan.pos), emit(plan.onSuccess), emit(plan.onFailure))
845+
else {
846+
/** Merge nested `if`s that have the same `else` branch into a single `if`.
847+
* This optimization targets calls to label defs for case failure jumps to next case.
848+
*/
849+
def emmitWithMashedConditions(plans: List[TestPlan]): Tree = {
850+
val plan = plans.head
851+
plan.onSuccess match {
852+
case plan2: TestPlan if plan.onFailure == plan2.onFailure =>
853+
emmitWithMashedConditions(plan2 :: plans)
854+
case _ =>
855+
def emitCondWithPos(plan: TestPlan) = emitCondition(plan).withPos(plan.pos)
856+
val conditions =
857+
plans.foldRight[Tree](EmptyTree) { (otherPlan, acc) =>
858+
if (acc.isEmpty) emitCondWithPos(otherPlan)
859+
else acc.select(nme.ZAND).appliedTo(emitCondWithPos(otherPlan))
860+
}
861+
If(conditions, emit(plan.onSuccess), emit(plan.onFailure))
862+
}
863+
864+
}
865+
emmitWithMashedConditions(plan :: Nil)
866+
}
842867
case LetPlan(sym, body) =>
843868
seq(ValDef(sym, initializer(sym).ensureConforms(sym.info)) :: Nil, emit(body))
844869
case LabelledPlan(label, body, params) =>

tests/pos/i2903.scala

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
case class Foo1(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
2+
case class Foo2(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
3+
case class Foo3(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
4+
case class Foo4(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
5+
case class Foo5(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
6+
case class Foo6(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
7+
case class Foo7(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
8+
case class Foo8(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
9+
case class Foo9(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
10+
case class Foo10(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
11+
case class Foo11(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
12+
case class Foo12(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
13+
case class Foo13(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
14+
case class Foo14(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
15+
case class Foo15(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
16+
case class Foo16(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
17+
case class Foo17(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
18+
case class Foo18(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
19+
case class Foo19(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
20+
case class Foo20(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
21+
case class Foo21(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
22+
case class Foo22(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
23+
case class Foo23(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
24+
case class Foo24(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
25+
case class Foo25(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
26+
case class Foo26(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
27+
case class Foo27(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
28+
case class Foo28(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
29+
case class Foo29(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
30+
case class Foo30(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
31+
case class Foo31(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
32+
case class Foo32(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
33+
case class Foo33(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
34+
case class Foo34(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
35+
case class Foo35(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
36+
case class Foo36(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
37+
case class Foo37(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
38+
case class Foo38(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
39+
case class Foo39(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
40+
case class Foo40(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
41+
42+
object Test {
43+
def stuff() = {}
44+
45+
def test(x: Any): Unit = x match {
46+
case Foo1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
47+
case Foo2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
48+
case Foo3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
49+
case Foo4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
50+
case Foo5(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
51+
case Foo6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
52+
case Foo7(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
53+
case Foo8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
54+
case Foo9(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
55+
case Foo10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
56+
case Foo11(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
57+
case Foo12(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
58+
case Foo13(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
59+
case Foo14(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
60+
case Foo15(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
61+
case Foo16(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
62+
case Foo17(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
63+
case Foo18(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
64+
case Foo19(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
65+
case Foo20(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
66+
case Foo21(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
67+
case Foo22(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
68+
case Foo23(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
69+
case Foo24(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
70+
case Foo25(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
71+
case Foo26(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
72+
case Foo27(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
73+
case Foo28(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
74+
case Foo29(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
75+
case Foo30(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
76+
case Foo31(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
77+
case Foo32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
78+
case Foo33(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
79+
case Foo34(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
80+
case Foo35(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
81+
case Foo36(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
82+
case Foo37(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
83+
case Foo38(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
84+
case Foo39(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
85+
case Foo40(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
86+
}
87+
}

0 commit comments

Comments
 (0)