diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index a562410b262c..9c1c0acd73b3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -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`. @@ -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) => diff --git a/tests/pos/i2903.scala b/tests/pos/i2903.scala new file mode 100644 index 000000000000..f870bd0f66d8 --- /dev/null +++ b/tests/pos/i2903.scala @@ -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() + } +}