Skip to content

Commit 5ee2b7e

Browse files
committed
Remove special treatment of SubstOnlyTreeMakers.
In scalac SubstOnlyTreeMakers were implemented using substitution, and didn't actually introduce new trees. Thus there was an optimization to remove them while generating code. This optimization led to scala#190. It is now removed.
1 parent a812450 commit 5ee2b7e

File tree

1 file changed

+32
-17
lines changed

1 file changed

+32
-17
lines changed

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

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,25 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
394394
override def toString = "B"+((body, matchPt))
395395
}
396396

397+
/**
398+
* In scalac for such block
399+
* x match {
400+
* case d => <body>
401+
* }
402+
*
403+
* d inside <body> was to be substitued by x.
404+
*
405+
* In dotty, SubstOnlyTreeMakers instead generate normal ValDef,
406+
* and does not create a new substitution.
407+
*
408+
* This was done for several reasons:
409+
* 1) it is a lot easyer to Y-check,
410+
* as d type could be used in <body>.
411+
* 2) it would simplify debugging of the generated code as
412+
* this works also for nested patterns, and previously they used unreadable names
413+
* 3) It showed better(~30%), performance,
414+
* Rebuilding tree and propagating types was taking substantial time.
415+
*/
397416
case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker {
398417
val pos = Positions.NoPosition
399418

@@ -851,29 +870,25 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
851870
def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker])
852871

853872
// a foldLeft to accumulate the localSubstitution left-to-right
854-
// it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution
873+
// unlike in scalace it does not drop SubstOnly tree makers,
874+
// as there could types having them as prefix
855875
def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = {
856876
var accumSubst: Substitution = initial
857877
treeMakers foreach { maker =>
858878
maker incorporateOuterSubstitution accumSubst
859879
accumSubst = maker.substitution
860880
}
861-
removeSubstOnly(treeMakers)
881+
treeMakers
862882
}
863883

864884
// calls propagateSubstitution on the treemakers
865885
def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Symbol => Tree]): Tree = {
866-
// drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
867-
val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution))
868-
combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, matchFailGenOverride)
869-
}
886+
// unlike in scalac SubstOnlyTreeMakers are maintained.
887+
val casesSubstitutionPropagated = casesRaw map (propagateSubstitution(_, EmptySubstitution))
870888

871-
// pt is the fully defined type of the cases (either pt or the lub of the types of the cases)
872-
def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Symbol => Tree]): Tree =
873-
/*fixerUpper(owner, scrut.pos)*/ {
874-
def matchFailGen = matchFailGenOverride orElse Some((arg: Symbol) => Throw(New(defn.MatchErrorType, List(ref(arg)))))
889+
def matchFailGen = matchFailGenOverride orElse Some((arg: Symbol) => Throw(New(defn.MatchErrorType, List(ref(arg)))))
875890

876-
ctx.debuglog("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}")))
891+
ctx.debuglog("combining cases: "+ (casesSubstitutionPropagated.map(_.mkString(" >> ")).mkString("{", "\n", "}")))
877892

878893
val (suppression, requireSwitch): (Suppression, Boolean) =
879894
/*if (settings.XnoPatmatAnalysis)*/ (Suppression.NoSuppression, false)
@@ -892,26 +907,26 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
892907
(Suppression.NoSuppression, false)
893908
}*/
894909

895-
emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{
910+
emitSwitch(scrut, scrutSym, casesSubstitutionPropagated, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{
896911
if (requireSwitch) ctx.warning("could not emit switch for @switch annotated match", scrut.pos)
897912

898-
if (casesNoSubstOnly nonEmpty) {
913+
if (casesSubstitutionPropagated nonEmpty) {
899914
// before optimizing, check casesNoSubstOnly for presence of a default case,
900915
// since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
901916
// exhaustivity and reachability must be checked before optimization as well
902917
// TODO: improve notion of trivial/irrefutable -- a trivial type test before the body still makes for a default case
903918
// ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op)
904919
// irrefutability checking should use the approximation framework also used for CSE, unreachability and exhaustivity checking
905920
val synthCatchAll: Option[Symbol => Tree] =
906-
if (casesNoSubstOnly.nonEmpty && {
907-
val nonTrivLast = casesNoSubstOnly.last
921+
if (casesSubstitutionPropagated.nonEmpty && {
922+
val nonTrivLast = casesSubstitutionPropagated.last
908923
nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker]
909924
}) None
910925
else matchFailGen
911926

912-
analyzeCases(scrutSym, casesNoSubstOnly, pt, suppression)
927+
analyzeCases(scrutSym, casesSubstitutionPropagated, pt, suppression)
913928

914-
val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt)
929+
val (cases, toHoist) = optimizeCases(scrutSym, casesSubstitutionPropagated, pt)
915930

916931
val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases.map(x => combineExtractors(x) _), synthCatchAll)
917932

0 commit comments

Comments
 (0)