Skip to content

Commit cfc08a0

Browse files
committed
basic support for switches
1 parent aba2a09 commit cfc08a0

File tree

1 file changed

+62
-53
lines changed

1 file changed

+62
-53
lines changed

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

Lines changed: 62 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.dotc
22
package transform
33

4+
import scala.annotation.tailrec
45
import core._
56
import MegaPhase._
67
import collection.mutable
@@ -757,47 +758,49 @@ object PatternMatcher {
757758
}
758759
}
759760

761+
@tailrec
762+
private def canFallThrough(plan: Plan): Boolean = plan match {
763+
case _:ReturnPlan | _:ResultPlan => false
764+
case _:TestPlan | _:LabeledPlan => true
765+
case LetPlan(_, body) => canFallThrough(body)
766+
case SeqPlan(_, tail) => canFallThrough(tail)
767+
}
768+
760769
/** Collect longest list of plans that represent possible cases of
761770
* a switch, including a last default case, by starting with this
762771
* plan and following onSuccess plans.
763772
*/
764-
/*
765-
private def collectSwitchCases(plan: TestPlan): List[Plan] = {
773+
private def collectSwitchCases(scrutinee: Tree, plan: SeqPlan): List[Plan] = {
766774
def isSwitchableType(tpe: Type): Boolean =
767775
(tpe isRef defn.IntClass) ||
768776
(tpe isRef defn.ByteClass) ||
769777
(tpe isRef defn.ShortClass) ||
770778
(tpe isRef defn.CharClass)
771779

772-
val scrutinee = plan.scrutinee
773-
774780
def isIntConst(tree: Tree) = tree match {
775781
case Literal(const) => const.isIntRange
776782
case _ => false
777783
}
778784

779785
def recur(plan: Plan): List[Plan] = plan match {
780-
case TestPlan(EqualTest(tree), scrut, _, _, onf)
781-
if scrut === scrutinee && isIntConst(tree) =>
782-
plan :: recur(onf)
786+
case SeqPlan(testPlan @ TestPlan(EqualTest(tree), scrut, _, ons), tail)
787+
if scrut === scrutinee && isIntConst(tree) && !canFallThrough(ons) =>
788+
testPlan :: recur(tail)
783789
case _ =>
784790
plan :: Nil
785791
}
786792

787793
if (isSwitchableType(scrutinee.tpe.widen)) recur(plan)
788794
else Nil
789795
}
790-
*/
791796

792797
/** Emit cases of a switch */
793-
/*
794798
private def emitSwitchCases(cases: List[Plan]): List[CaseDef] = (cases: @unchecked) match {
795799
case (default: Plan) :: Nil =>
796800
CaseDef(Underscore(defn.IntType), EmptyTree, emit(default)) :: Nil
797-
case TestPlan(EqualTest(tree), _, _, ons, _) :: cases1 =>
801+
case TestPlan(EqualTest(tree), _, _, ons) :: cases1 =>
798802
CaseDef(tree, EmptyTree, emit(ons)) :: emitSwitchCases(cases1)
799803
}
800-
*/
801804

802805
/** If selfCheck is `true`, used to check whether a tree gets generated twice */
803806
private val emitted = mutable.Set[Int]()
@@ -810,55 +813,61 @@ object PatternMatcher {
810813
}
811814
plan match {
812815
case plan: TestPlan =>
813-
/*val switchCases = collectSwitchCases(plan)
814-
if (switchCases.lengthCompare(MinSwitchCases) >= 0) // at least 3 cases + default
815-
Match(plan.scrutinee, emitSwitchCases(switchCases))
816-
else*/ {
817-
/** Merge nested `if`s that have the same `else` branch into a single `if`.
818-
* This optimization targets calls to label defs for case failure jumps to next case.
819-
*
820-
* Plan for
821-
* ```
822-
* val x1: Int = ...
823-
* val x2: Int = ...
824-
* if (x1 == y1) {
825-
* if (x2 == y2) someCode
826-
* else label$1()
827-
* } else label$1()
828-
* ```
829-
* is emitted as
830-
* ```
831-
* val x1: Int = ...
832-
* val x2: Int = ...
833-
* if (x1 == y1 && x2 == y2) someCode
834-
* else label$1()
835-
* ```
836-
*/
837-
def emitWithMashedConditions(plans: List[TestPlan]): Tree = {
838-
val plan = plans.head
839-
plan.onSuccess match {
840-
case plan2: TestPlan =>
841-
emitWithMashedConditions(plan2 :: plans)
842-
case _ =>
843-
def emitCondWithPos(plan: TestPlan) = emitCondition(plan).withPos(plan.pos)
844-
val conditions =
845-
plans.foldRight[Tree](EmptyTree) { (otherPlan, acc) =>
846-
if (acc.isEmpty) emitCondWithPos(otherPlan)
847-
else acc.select(nme.ZAND).appliedTo(emitCondWithPos(otherPlan))
848-
}
849-
If(conditions, emit(plan.onSuccess), unitLiteral)
850-
}
816+
/** Merge nested `if`s that have the same `else` branch into a single `if`.
817+
* This optimization targets calls to label defs for case failure jumps to next case.
818+
*
819+
* Plan for
820+
* ```
821+
* val x1: Int = ...
822+
* val x2: Int = ...
823+
* if (x1 == y1) {
824+
* if (x2 == y2) someCode
825+
* else label$1()
826+
* } else label$1()
827+
* ```
828+
* is emitted as
829+
* ```
830+
* val x1: Int = ...
831+
* val x2: Int = ...
832+
* if (x1 == y1 && x2 == y2) someCode
833+
* else label$1()
834+
* ```
835+
*/
836+
def emitWithMashedConditions(plans: List[TestPlan]): Tree = {
837+
val plan = plans.head
838+
plan.onSuccess match {
839+
case plan2: TestPlan =>
840+
emitWithMashedConditions(plan2 :: plans)
841+
case _ =>
842+
def emitCondWithPos(plan: TestPlan) = emitCondition(plan).withPos(plan.pos)
843+
val conditions =
844+
plans.foldRight[Tree](EmptyTree) { (otherPlan, acc) =>
845+
if (acc.isEmpty) emitCondWithPos(otherPlan)
846+
else acc.select(nme.ZAND).appliedTo(emitCondWithPos(otherPlan))
847+
}
848+
If(conditions, emit(plan.onSuccess), unitLiteral)
851849
}
852-
emitWithMashedConditions(plan :: Nil)
853850
}
851+
emitWithMashedConditions(plan :: Nil)
854852
case LetPlan(sym, body) =>
855853
seq(ValDef(sym, initializer(sym).ensureConforms(sym.info)) :: Nil, emit(body))
856854
case LabeledPlan(label, expr) =>
857855
Labeled(label, emit(expr))
858856
case ReturnPlan(label) =>
859857
Return(Literal(Constant(())), ref(label))
860-
case SeqPlan(head, tail) =>
861-
seq(emit(head) :: Nil, emit(tail))
858+
case plan: SeqPlan =>
859+
def default = seq(emit(plan.head) :: Nil, emit(plan.tail))
860+
plan.head match {
861+
case testPlan: TestPlan =>
862+
val scrutinee = testPlan.scrutinee
863+
val switchCases = collectSwitchCases(scrutinee, plan)
864+
if (switchCases.lengthCompare(MinSwitchCases) >= 0) // at least 3 cases + default
865+
Match(scrutinee, emitSwitchCases(switchCases))
866+
else
867+
default
868+
case _ =>
869+
default
870+
}
862871
case ResultPlan(tree) =>
863872
Return(tree, ref(resultLabel))
864873
}
@@ -954,7 +963,7 @@ object PatternMatcher {
954963
//System.err.println(s"After $title: ${show(plan)}")
955964
}
956965
val result = emit(plan)
957-
//checkSwitch(tree, result)
966+
checkSwitch(tree, result)
958967
Labeled(resultLabel, result)
959968
}
960969
}

0 commit comments

Comments
 (0)