@@ -103,6 +103,13 @@ object PatternMatcher {
103
103
LabeledPlan (label, expr(ReturnPlan (label)), next)
104
104
}
105
105
106
+ /** The plan `let l = labelled in body(l)` where `l` is a fresh label */
107
+ private def labeledAbstract2 (next : Plan )(expr : TermSymbol => Plan ): Plan = {
108
+ val label = ctx.newSymbol(ctx.owner, PatMatCaseName .fresh(), Synthetic | Label ,
109
+ defn.UnitType )
110
+ LabeledPlan (label, expr(label), next)
111
+ }
112
+
106
113
/** Test whether a type refers to a pattern-generated variable */
107
114
private val refersToInternal = new TypeAccumulator [Boolean ] {
108
115
def apply (x : Boolean , tp : Type ) =
@@ -141,7 +148,7 @@ object PatternMatcher {
141
148
142
149
case class LetPlan (sym : TermSymbol , var body : Plan ) extends Plan
143
150
case class LabeledPlan (sym : TermSymbol , var expr : Plan , var next : Plan ) extends Plan
144
- case class ReturnPlan (label : TermSymbol ) extends Plan
151
+ case class ReturnPlan (var label : TermSymbol ) extends Plan
145
152
case class ResultPlan (var tree : Tree ) extends Plan
146
153
147
154
object TestPlan {
@@ -459,6 +466,111 @@ object PatternMatcher {
459
466
refCounter.count
460
467
}
461
468
469
+ /** Merge identical tests from consecutive cases.
470
+ *
471
+ * When we have the following shape:
472
+ *
473
+ * caseM: {
474
+ * if (testA) plan1 else plan2
475
+ * }
476
+ * caseN: {
477
+ * if (testA) plan3 else plan4
478
+ * }
479
+ * nextPlan
480
+ *
481
+ * transform it to
482
+ *
483
+ * caseN: {
484
+ * if (testA) {
485
+ * case M: {
486
+ * plan1
487
+ * }
488
+ * plan3
489
+ * } else {
490
+ * case M2: {
491
+ * plan2[caseM2/caseM]
492
+ * }
493
+ * plan4
494
+ * }
495
+ * }
496
+ * nextPlan
497
+ *
498
+ * where plan2[caseM2/caseM] means substituting caseM2 for caseM in plan2.
499
+ *
500
+ * We use some tricks to identify a let pointing to an unapply and the
501
+ * NonEmptyTest that follows it as a single `UnappTest` test.
502
+ */
503
+ def mergeTests (plan : Plan ): Plan = {
504
+ def isUnapply (sym : Symbol ) = sym.name == nme.unapply || sym.name == nme.unapplySeq
505
+
506
+ /** A locally used test value that represents combos of
507
+ *
508
+ * let x = X.unapply(...) in if !x.isEmpty then ... else ...
509
+ */
510
+ case object UnappTest extends Test
511
+
512
+ /** If `plan` is the NonEmptyTest part of an unapply, the corresponding UnappTest
513
+ * otherwise the original plan
514
+ */
515
+ def normalize (plan : TestPlan ): TestPlan = plan.scrutinee match {
516
+ case id : Ident
517
+ if plan.test == NonEmptyTest &&
518
+ isPatmatGenerated(id.symbol) &&
519
+ isUnapply(initializer(id.symbol).symbol) =>
520
+ TestPlan (UnappTest , initializer(id.symbol), plan.pos, plan.onSuccess, plan.onFailure)
521
+ case _ =>
522
+ plan
523
+ }
524
+
525
+ /** Extractor for Let/NonEmptyTest combos that represent unapplies */
526
+ object UnappTestPlan {
527
+ def unapply (plan : Plan ): Option [TestPlan ] = plan match {
528
+ case LetPlan (sym, body : TestPlan ) =>
529
+ val RHS = initializer(sym)
530
+ normalize(body) match {
531
+ case normPlan @ TestPlan (UnappTest , RHS , _, _, _) => Some (normPlan)
532
+ case _ => None
533
+ }
534
+ case _ => None
535
+ }
536
+ }
537
+
538
+ class SubstituteLabel (from : TermSymbol , to : TermSymbol ) extends PlanTransform {
539
+ override def apply (plan : ReturnPlan ): Plan = {
540
+ if (plan.label == from)
541
+ plan.label = to
542
+ plan
543
+ }
544
+ }
545
+
546
+ class MergeTests extends PlanTransform {
547
+ override def apply (plan : LabeledPlan ): Plan = {
548
+ plan.next = apply(plan.next)
549
+ plan match {
550
+ case LabeledPlan (label1, testPlan1 : TestPlan , LabeledPlan (label2, testPlan2 : TestPlan , nextNext)) =>
551
+ val normTestPlan1 = normalize(testPlan1)
552
+ val normTestPlan2 = normalize(testPlan2)
553
+ if (normTestPlan1 == normTestPlan2) {
554
+ val onFailure = labeledAbstract2(testPlan2.onFailure) { label12 =>
555
+ new SubstituteLabel (label1, label12)(testPlan1.onFailure)
556
+ }
557
+ val onSuccess = LabeledPlan (label1, testPlan1.onSuccess, testPlan2.onSuccess)
558
+ testPlan1.onSuccess = apply(onSuccess)
559
+ testPlan1.onFailure = apply(onFailure)
560
+ LabeledPlan (label2, testPlan1, nextNext)
561
+ } else {
562
+ plan.expr = apply(plan.expr)
563
+ plan
564
+ }
565
+ case _ =>
566
+ plan.expr = apply(plan.expr)
567
+ plan
568
+ }
569
+ }
570
+ }
571
+ new MergeTests ()(plan)
572
+ }
573
+
462
574
/** Eliminate tests that are redundant (known to be true or false).
463
575
* Two parts:
464
576
*
@@ -949,6 +1061,7 @@ object PatternMatcher {
949
1061
}
950
1062
951
1063
val optimizations : List [(String , Plan => Plan )] = List (
1064
+ // "mergeTests" -> mergeTests
952
1065
/*
953
1066
"hoistLabels" -> hoistLabels,
954
1067
"elimRedundantTests" -> elimRedundantTests,
@@ -961,11 +1074,13 @@ object PatternMatcher {
961
1074
/** Translate pattern match to sequence of tests. */
962
1075
def translateMatch (tree : Match ): Tree = {
963
1076
var plan = matchPlan(tree)
964
- patmatch.println(i " Plan for $tree: ${show(plan)}" )
1077
+ // patmatch.println(i"Plan for $tree: ${show(plan)}")
1078
+ System .err.println(i " Plan for $tree: ${show(plan)}" )
965
1079
if (! ctx.settings.YnoPatmatOpt .value)
966
1080
for ((title, optimization) <- optimizations) {
967
1081
plan = optimization(plan)
968
- patmatch.println(s " After $title: ${show(plan)}" )
1082
+ // patmatch.println(s"After $title: ${show(plan)}")
1083
+ System .err.println(s " After $title: ${show(plan)}" )
969
1084
}
970
1085
val result = emit(plan)
971
1086
// checkSwitch(tree, result)
0 commit comments