Skip to content

Commit 64021f9

Browse files
authored
Merge pull request #3575 from dotty-staging/fix-#2903
Fix #2903: Reduce the depth of trees generated in PatternMatcher
2 parents 95418f9 + 90c09a1 commit 64021f9

File tree

2 files changed

+159
-10
lines changed

2 files changed

+159
-10
lines changed

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

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,39 @@ 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+
/* For a case with arguments that have some test on them such as
217+
* ```
218+
* case Foo(1, 2) => someCode
219+
* ```
220+
* all arguments values are extracted before the checks are performed. This shape is expected by `emit`
221+
* to avoid generating deep trees.
222+
* ```
223+
* val x1: Foo = ...
224+
* val x2: Int = x1._1
225+
* val x3: Int = x1._2
226+
* if (x2 == 1) {
227+
* if (x3 == 2) someCode
228+
* else label$1()
229+
* } else label$1()
230+
* ```
231+
*/
232+
def matchArgsSelectorsPlan(selectors: List[Tree], syms: List[Symbol]): Plan =
233+
selectors match {
234+
case selector :: selectors1 => letAbstract(selector)(sym => matchArgsSelectorsPlan(selectors1, sym :: syms))
235+
case Nil => matchArgsPatternPlan(args, syms.reverse)
236+
}
237+
def matchArgsPatternPlan(args: List[Tree], syms: List[Symbol]): Plan =
238+
args match {
239+
case arg :: args1 =>
240+
val sym :: syms1 = syms
241+
patternPlan(sym, arg, matchArgsPatternPlan(args1, syms1), onFailure)
242+
case Nil =>
243+
assert(syms.isEmpty)
244+
onSuccess
245+
}
246+
matchArgsSelectorsPlan(selectors, Nil)
247+
}
223248

224249
/** Plan for matching the sequence in `seqSym` against sequence elements `args`.
225250
* If `exact` is true, the sequence is not permitted to have any elements following `args`.
@@ -838,8 +863,45 @@ object PatternMatcher {
838863
val switchCases = collectSwitchCases(plan)
839864
if (switchCases.lengthCompare(4) >= 0) // at least 3 cases + default
840865
Match(plan.scrutinee, emitSwitchCases(switchCases))
841-
else
842-
If(emitCondition(plan).withPos(plan.pos), emit(plan.onSuccess), emit(plan.onFailure))
866+
else {
867+
/** Merge nested `if`s that have the same `else` branch into a single `if`.
868+
* This optimization targets calls to label defs for case failure jumps to next case.
869+
*
870+
* Plan for
871+
* ```
872+
* val x1: Int = ...
873+
* val x2: Int = ...
874+
* if (x1 == y1) {
875+
* if (x2 == y2) someCode
876+
* else label$1()
877+
* } else label$1()
878+
* ```
879+
* is emitted as
880+
* ```
881+
* val x1: Int = ...
882+
* val x2: Int = ...
883+
* if (x1 == y1 && x2 == y2) someCode
884+
* else label$1()
885+
* ```
886+
*/
887+
def emitWithMashedConditions(plans: List[TestPlan]): Tree = {
888+
val plan = plans.head
889+
plan.onSuccess match {
890+
case plan2: TestPlan if plan.onFailure == plan2.onFailure =>
891+
emitWithMashedConditions(plan2 :: plans)
892+
case _ =>
893+
def emitCondWithPos(plan: TestPlan) = emitCondition(plan).withPos(plan.pos)
894+
val conditions =
895+
plans.foldRight[Tree](EmptyTree) { (otherPlan, acc) =>
896+
if (acc.isEmpty) emitCondWithPos(otherPlan)
897+
else acc.select(nme.ZAND).appliedTo(emitCondWithPos(otherPlan))
898+
}
899+
If(conditions, emit(plan.onSuccess), emit(plan.onFailure))
900+
}
901+
902+
}
903+
emitWithMashedConditions(plan :: Nil)
904+
}
843905
case LetPlan(sym, body) =>
844906
seq(ValDef(sym, initializer(sym).ensureConforms(sym.info)) :: Nil, emit(body))
845907
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)