Skip to content

Fix #2903: Reduce the depth of trees generated in PatternMatcher #3575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 72 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,39 @@ object PatternMatcher {
private def patternPlan(scrutinee: Symbol, tree: Tree, onSuccess: Plan, onFailure: Plan): Plan = {

/** Plan for matching `selectors` against argument patterns `args` */
def matchArgsPlan(selectors: List[Tree], args: List[Tree], onSuccess: Plan): Plan =
args match {
case arg :: args1 =>
val selector :: selectors1 = selectors
letAbstract(selector)(
patternPlan(_, arg, matchArgsPlan(selectors1, args1, onSuccess), onFailure))
case Nil => onSuccess
}
def matchArgsPlan(selectors: List[Tree], args: List[Tree], onSuccess: Plan): Plan = {
/* For a case with arguments that have some test on them such as
* ```
* case Foo(1, 2) => someCode
* ```
* all arguments values are extracted before the checks are performed. This shape is expected by `emit`
* to avoid generating deep trees.
* ```
* val x1: Foo = ...
* val x2: Int = x1._1
* val x3: Int = x1._2
* if (x2 == 1) {
* if (x3 == 2) someCode
* else label$1()
* } else label$1()
* ```
*/
def matchArgsSelectorsPlan(selectors: List[Tree], syms: List[Symbol]): Plan =
selectors match {
case selector :: selectors1 => letAbstract(selector)(sym => matchArgsSelectorsPlan(selectors1, sym :: syms))
case Nil => matchArgsPatternPlan(args, syms.reverse)
}
def matchArgsPatternPlan(args: List[Tree], syms: List[Symbol]): Plan =
args match {
case arg :: args1 =>
val sym :: syms1 = syms
patternPlan(sym, arg, matchArgsPatternPlan(args1, syms1), onFailure)
case Nil =>
assert(syms.isEmpty)
onSuccess
}
matchArgsSelectorsPlan(selectors, Nil)
}

/** Plan for matching the sequence in `seqSym` against sequence elements `args`.
* If `exact` is true, the sequence is not permitted to have any elements following `args`.
Expand Down Expand Up @@ -838,8 +863,45 @@ object PatternMatcher {
val switchCases = collectSwitchCases(plan)
if (switchCases.lengthCompare(4) >= 0) // at least 3 cases + default
Match(plan.scrutinee, emitSwitchCases(switchCases))
else
If(emitCondition(plan).withPos(plan.pos), emit(plan.onSuccess), emit(plan.onFailure))
else {
/** Merge nested `if`s that have the same `else` branch into a single `if`.
* This optimization targets calls to label defs for case failure jumps to next case.
*
* Plan for
* ```
* val x1: Int = ...
* val x2: Int = ...
* if (x1 == y1) {
* if (x2 == y2) someCode
* else label$1()
* } else label$1()
* ```
* is emitted as
* ```
* val x1: Int = ...
* val x2: Int = ...
* if (x1 == y1 && x2 == y2) someCode
* else label$1()
* ```
*/
def emitWithMashedConditions(plans: List[TestPlan]): Tree = {
val plan = plans.head
plan.onSuccess match {
case plan2: TestPlan if plan.onFailure == plan2.onFailure =>
emitWithMashedConditions(plan2 :: plans)
case _ =>
def emitCondWithPos(plan: TestPlan) = emitCondition(plan).withPos(plan.pos)
val conditions =
plans.foldRight[Tree](EmptyTree) { (otherPlan, acc) =>
if (acc.isEmpty) emitCondWithPos(otherPlan)
else acc.select(nme.ZAND).appliedTo(emitCondWithPos(otherPlan))
}
If(conditions, emit(plan.onSuccess), emit(plan.onFailure))
}

}
emitWithMashedConditions(plan :: Nil)
}
case LetPlan(sym, body) =>
seq(ValDef(sym, initializer(sym).ensureConforms(sym.info)) :: Nil, emit(body))
case LabelledPlan(label, body, params) =>
Expand Down
87 changes: 87 additions & 0 deletions tests/pos/i2903.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
case class Foo1(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo2(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo3(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo4(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo5(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo6(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo7(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo8(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo9(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo10(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo11(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo12(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo13(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo14(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo15(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo16(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo17(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo18(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo19(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo20(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo21(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo22(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo23(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo24(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo25(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo26(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo27(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo28(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo29(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo30(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo31(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo32(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo33(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo34(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo35(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo36(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo37(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo38(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo39(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)
case class Foo40(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int)

object Test {
def stuff() = {}

def test(x: Any): Unit = x match {
case Foo1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo5(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo7(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo9(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo11(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo12(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo13(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo14(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo15(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo16(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo17(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo18(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo19(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo20(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo21(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo22(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo23(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo24(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo25(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo26(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo27(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo28(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo29(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo30(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo31(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo33(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo34(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo35(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo36(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo37(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo38(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo39(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
case Foo40(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) => stuff()
}
}