Skip to content

Commit 0bc8f29

Browse files
committed
First version of merging.
1 parent 6ff4671 commit 0bc8f29

File tree

1 file changed

+57
-86
lines changed

1 file changed

+57
-86
lines changed

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

Lines changed: 57 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -474,112 +474,83 @@ object PatternMatcher {
474474
refCounter.count
475475
}
476476

477-
/** Merge identical tests from consecutive cases.
477+
/** Merge identical consecutive tests.
478478
*
479479
* When we have the following shape:
480480
*
481-
* caseM: {
482-
* if (testA) plan1 else plan2
483-
* }
484-
* caseN: {
485-
* if (testA) plan3 else plan4
486-
* }
487-
* nextPlan
481+
* if (testA) plan1
482+
* if (testA) plan2
483+
* nextPlan?
488484
*
489485
* transform it to
490486
*
491-
* caseN: {
492-
* if (testA) {
493-
* case M: {
494-
* plan1
495-
* }
496-
* plan3
497-
* } else {
498-
* case M2: {
499-
* plan2[caseM2/caseM]
500-
* }
501-
* plan4
502-
* }
487+
* if (testA) {
488+
* plan1
489+
* plan2
503490
* }
504-
* nextPlan
491+
* nextPlan?
505492
*
506-
* where plan2[caseM2/caseM] means substituting caseM2 for caseM in plan2.
493+
* Similarly, when we have equivalent let bindings:
507494
*
508-
* We use some tricks to identify a let pointing to an unapply and the
509-
* NonEmptyTest that follows it as a single `UnappTest` test.
495+
* let x1 = rhs1 in plan1
496+
* let x2 = rhs2 in plan2
497+
* nextPlan?
498+
*
499+
* and rhs1 and rhs2 are equivalent, transform it to
500+
*
501+
* let x1 = rhs1 in {
502+
* plan1
503+
* plan2[x1/x2]
504+
* }
505+
*
506+
* where plan2[x1/x2] means substituting x1 for x2 in plan2.
510507
*/
511-
/*
512508
def mergeTests(plan: Plan): Plan = {
513-
def isUnapply(sym: Symbol) = sym.name == nme.unapply || sym.name == nme.unapplySeq
514-
515-
/** A locally used test value that represents combos of
516-
*
517-
* let x = X.unapply(...) in if !x.isEmpty then ... else ...
518-
*/
519-
case object UnappTest extends Test
520-
521-
/** If `plan` is the NonEmptyTest part of an unapply, the corresponding UnappTest
522-
* otherwise the original plan
523-
*/
524-
def normalize(plan: TestPlan): TestPlan = plan.scrutinee match {
525-
case id: Ident
526-
if plan.test == NonEmptyTest &&
527-
isPatmatGenerated(id.symbol) &&
528-
isUnapply(initializer(id.symbol).symbol) =>
529-
TestPlan(UnappTest, initializer(id.symbol), plan.pos, plan.onSuccess, plan.onFailure)
530-
case _ =>
531-
plan
532-
}
533-
534-
/** Extractor for Let/NonEmptyTest combos that represent unapplies */
535-
object UnappTestPlan {
536-
def unapply(plan: Plan): Option[TestPlan] = plan match {
537-
case LetPlan(sym, body: TestPlan) =>
538-
val RHS = initializer(sym)
539-
normalize(body) match {
540-
case normPlan @ TestPlan(UnappTest, RHS, _, _, _) => Some(normPlan)
541-
case _ => None
542-
}
543-
case _ => None
544-
}
545-
}
546-
547-
class SubstituteLabel(from: TermSymbol, to: TermSymbol) extends PlanTransform {
548-
override def apply(plan: ReturnPlan): Plan = {
549-
if (plan.label == from)
550-
plan.label = to
551-
plan
509+
class SubstituteIdent(from: TermSymbol, to: TermSymbol) extends PlanTransform {
510+
override val treeMap = new TreeMap {
511+
override def transform(tree: Tree)(implicit ctx: Context) = tree match {
512+
case tree: Ident if tree.symbol == from => ref(to)
513+
case _ => super.transform(tree)
514+
}
552515
}
553516
}
554517

555518
class MergeTests extends PlanTransform {
556-
override def apply(plan: LabeledPlan): Plan = {
557-
plan.next = apply(plan.next)
558-
plan match {
559-
case LabeledPlan(label1, testPlan1: TestPlan, LabeledPlan(label2, testPlan2: TestPlan, nextNext)) =>
560-
val normTestPlan1 = normalize(testPlan1)
561-
val normTestPlan2 = normalize(testPlan2)
562-
if (normTestPlan1 == normTestPlan2) {
563-
val onFailure = labeledAbstract2(testPlan2.onFailure) { label12 =>
564-
new SubstituteLabel(label1, label12)(testPlan1.onFailure)
565-
}
566-
val onSuccess = LabeledPlan(label1, testPlan1.onSuccess, testPlan2.onSuccess)
567-
testPlan1.onSuccess = apply(onSuccess)
568-
testPlan1.onFailure = apply(onFailure)
569-
LabeledPlan(label2, testPlan1, nextNext)
570-
} else {
571-
plan.expr = apply(plan.expr)
572-
plan
519+
override def apply(plan: SeqPlan): Plan = {
520+
def tryMerge(plan1: Plan, plan2: Plan): Option[Plan] = {
521+
(plan1, plan2) match {
522+
case (plan1: TestPlan, plan2: TestPlan) if plan1 == plan2 =>
523+
plan1.onSuccess = SeqPlan(plan1.onSuccess, plan2.onSuccess)
524+
Some(plan1)
525+
526+
case (plan1: LetPlan, plan2: LetPlan) if isPatmatGenerated(plan2.sym) && initializer(plan1.sym) === initializer(plan2.sym) =>
527+
val newPlan2Body = new SubstituteIdent(plan2.sym, plan1.sym)(plan2.body)
528+
plan1.body = SeqPlan(plan1.body, newPlan2Body)
529+
Some(plan1)
530+
531+
case _ =>
532+
None
533+
}
534+
}
535+
536+
plan.head = apply(plan.head)
537+
plan.tail = apply(plan.tail)
538+
plan.tail match {
539+
case SeqPlan(tailHead, tailTail) =>
540+
tryMerge(plan.head, tailHead) match {
541+
case Some(merged) => SeqPlan(apply(merged), tailTail)
542+
case none => plan
543+
}
544+
case tail =>
545+
tryMerge(plan.head, tail) match {
546+
case Some(merged) => apply(merged)
547+
case none => plan
573548
}
574-
case _ =>
575-
plan.expr = apply(plan.expr)
576-
plan
577549
}
578550
}
579551
}
580552
new MergeTests()(plan)
581553
}
582-
*/
583554

584555
/** Eliminate tests that are redundant (known to be true or false).
585556
* Two parts:
@@ -1077,7 +1048,7 @@ object PatternMatcher {
10771048
}
10781049

10791050
val optimizations: List[(String, Plan => Plan)] = List(
1080-
//"mergeTests" -> mergeTests
1051+
"mergeTests" -> mergeTests
10811052
/*
10821053
"hoistLabels" -> hoistLabels,
10831054
"elimRedundantTests" -> elimRedundantTests,

0 commit comments

Comments
 (0)