summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala116
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala330
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala16
-rw-r--r--test/files/pos/virtpatmat_alts_subst.flags1
-rw-r--r--test/files/pos/virtpatmat_alts_subst.scala6
-rw-r--r--test/files/pos/virtpatmat_binding_opt.flags1
-rw-r--r--test/files/pos/virtpatmat_binding_opt.scala11
-rw-r--r--test/files/run/virtpatmat_literal.scala3
9 files changed, 319 insertions, 166 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index e26a0d59e8..3f0a0fac1a 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -1078,6 +1078,7 @@ abstract class GenICode extends SubComponent {
}
caseCtx = genLoad(body, tmpCtx, generatedType)
+ // close the block unless it's already been closed by the body, which closes the block if it ends in a jump (which is emitted to have alternatives share their body)
caseCtx.bb.closeWith(JUMP(afterCtx.bb) setPos caze.pos)
}
ctx1.bb.emitOnly(
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 6ac28f2fe3..90f46206c5 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -290,38 +290,70 @@ abstract class UnCurry extends InfoTransform
val idparam = m.paramss.head.head
val substParam = new TreeSymSubstituter(List(vparam), List(idparam))
def substTree[T <: Tree](t: T): T = substParam(resetLocalAttrs(t))
- object VirtPatmatOpt {
- object Last {
- def unapply[T](xs: List[T]) = xs.lastOption
- }
- // keep this in synch by what's generated by combineCases/runOrElse
- object MatcherBlock {
- def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree])] = matcher match { // TODO: BUG the unapplySeq version of the case below does not seem to work in virtpatmat??
- case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) => Some(zero, x, matchRes, keepGoing, stats)
- case _ => None
+
+ // waiting here until we can mix case classes and extractors reliably (i.e., when virtpatmat becomes the default)
+ // object VirtPatmatOpt {
+ // object Last {
+ // def unapply[T](xs: List[T]) = xs.lastOption
+ // }
+ // // keep this in synch by what's generated by combineCases/runOrElse
+ // object MatcherBlock {
+ // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree])] = matcher match { // TODO: BUG the unapplySeq version of the case below does not seem to work in virtpatmat??
+ // case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) => Some(zero, x, matchRes, keepGoing, stats)
+ // case _ => None
+ // }
+ // }
+ // // TODO: virtpatmat use case: would be nice if could abstract over the repeated pattern more easily
+ // // case Block(Last(P)) =>
+ // // case P =>
+ // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree], Tree => Tree)] = matcher match {
+ // case MatcherBlock(zero, x, matchRes, keepGoing, stats) => Some(zero, x, matchRes, keepGoing, stats, identity[Tree])
+ // case Block(outerStats, MatcherBlock(zero, x, matchRes, keepGoing, stats)) => Some(zero, x, matchRes, keepGoing, stats, inner => Block(outerStats, inner))
+ // case b => treeBrowser browse b; None
+ // }
+ // }
+
+ // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly
+ def dupMatch(selector: Tree, cases: List[CaseDef], wrap: Match => Tree = identity) = {
+ def transformCase(cdef: CaseDef): CaseDef =
+ CaseDef(cdef.pat, cdef.guard, Literal(Constant(true)))
+ def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
+
+ gen.mkUncheckedMatch(
+ if (cases exists treeInfo.isDefaultCase) Literal(Constant(true))
+ else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate)
+ )
+ }
+
+ def dupVirtMatch(zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], wrap: Block => Tree = identity) = {
+ object dropMatchResAssign extends Transformer {
+ // override val treeCopy = newStrictTreeCopier // will duplicate below
+ override def transform(tree: Tree): Tree = tree match {
+ // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing
+ case Block(List(matchRes, ass@Assign(keepGoingLhs, falseLit)), zero) if keepGoingLhs.symbol eq keepGoing.symbol =>
+ Block(List(ass), zero)
+ case _ =>
+ super.transform(tree)
}
}
- // TODO: virtpatmat use case: would be nice if could abstract over the repeated pattern more easily
- // case Block(Last(P)) =>
- // case P =>
- def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree], Tree => Tree)] = matcher match {
- case MatcherBlock(zero, x, matchRes, keepGoing, stats) => Some(zero, x, matchRes, keepGoing, stats, identity[Tree])
- case Block(outerStats, MatcherBlock(zero, x, matchRes, keepGoing, stats)) => Some(zero, x, matchRes, keepGoing, stats, inner => Block(outerStats, inner))
- case b => treeBrowser browse b; None
- }
+ val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList
+ val idaBlock = wrap(Block(
+ zero ::
+ x ::
+ /* drop matchRes def */
+ keepGoing ::
+ statsNoMatchRes,
+ NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `!keepGoing`
+ ))
+ substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed
}
+
DefDef(m, (fun.body: @unchecked) match {
case Match(selector, cases) =>
- def transformCase(cdef: CaseDef): CaseDef =
- substTree(CaseDef(cdef.pat.duplicate, cdef.guard.duplicate, Literal(Constant(true))))
- def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
-
- gen.mkUncheckedMatch(
- if (cases exists treeInfo.isDefaultCase) Literal(Constant(true))
- else Match(substTree(selector.duplicate), (cases map transformCase) :+ defaultCase)
- )
- // TODO: find a better way to keep this in synch with the code generard by patmatvirtualizer
- // TODO: check tgt.tpe.typeSymbol isNonBottomSubclass MatchingStrategyClass
+ dupMatch(selector, cases)
+ case Block((vd: ValDef) :: Nil, Match(selector, cases)) => // can't factor this out using an extractor due to bugs in the old pattern matcher
+ dupMatch(selector, cases, m => Block(List(vd), m))
+ // virtpatmat -- TODO: find a better way to keep this in synch with the code generated by patmatvirtualizer
case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), args_scrut), args_pm) if opt.virtPatmat =>
object noOne extends Transformer {
override val treeCopy = newStrictTreeCopier // must duplicate everything
@@ -336,28 +368,13 @@ abstract class UnCurry extends InfoTransform
}
}
substTree(Apply(Apply(TypeApply(Select(tgt.duplicate, tgt.tpe.member("isSuccess".toTermName)), targs map (_.duplicate)), args_scrut map (_.duplicate)), args_pm map (noOne.transform)))
- // for no-option version of virtpatmat
- case VirtPatmatOpt(zero, x, matchRes, keepGoing, stats, addOuter) if opt.virtPatmat => import CODE._
- object dropMatchResAssign extends Transformer {
- // override val treeCopy = newStrictTreeCopier // will duplicate below
- override def transform(tree: Tree): Tree = tree match {
- // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing
- case Block(List(matchRes, ass@Assign(keepGoingLhs, falseLit)), zero) if keepGoingLhs.symbol eq keepGoing.symbol =>
- Block(List(ass), zero)
- case _ =>
- super.transform(tree)
- }
- }
- val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList
- val idaBlock = addOuter(Block(
- zero ::
- x ::
- /* drop matchRes def */
- keepGoing ::
- statsNoMatchRes,
- NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `!keepGoing`
- ))
- substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed
+ // for the optimized version of virtpatmat
+ case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) if opt.virtPatmat =>
+ dupVirtMatch(zero, x, matchRes, keepGoing, stats)
+ case Block(outerStats, Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _)) if opt.virtPatmat => // can't factor this out using an extractor due to bugs in the old pattern matcher
+ dupVirtMatch(zero, x, matchRes, keepGoing, stats, m => Block(outerStats, m))
+ // case other =>
+ // treeBrowser browse other
})
}
@@ -542,6 +559,7 @@ abstract class UnCurry extends InfoTransform
}
case ValDef(_, _, _, rhs) =>
val sym = tree.symbol
+ if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit)
// a local variable that is mutable and free somewhere later should be lifted
// as lambda lifting (coming later) will wrap 'rhs' in an Ref object.
if (!sym.owner.isSourceMethod)
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
index 17f2d4f96c..ff59cb15f1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
@@ -90,7 +90,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
val scrutSym = freshSym(scrut.pos, scrutType)
val okPt = repeatedToSeq(pt)
// pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental
- fixerUpper(context.owner, scrut.pos)(combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt))
+ combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, context.owner)
}
@@ -123,7 +123,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
*
*/
def translateCase(scrutSym: Symbol, pt: Type)(caseDef: CaseDef) = caseDef match { case CaseDef(pattern, guard, body) =>
- (translatePattern(scrutSym, pattern) ++ translateGuard(guard), translateBody(body, pt))
+ translatePattern(scrutSym, pattern) ++ translateGuard(guard) :+ translateBody(body, pt)
}
def translatePattern(patBinder: Symbol, patTree: Tree): List[TreeMaker] = {
@@ -274,11 +274,13 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// so that we can return Option's from a match without ambiguity whether this indicates failure in the monad, or just some result in the monad
// 2) body.tpe is the type of the body after applying the substitution that represents the solution of GADT type inference
// need the explicit cast in case our substitutions in the body change the type to something that doesn't take GADT typing into account
- def translateBody(body: Tree, matchPt: Type): Tree = atPos(body.pos)(pmgen.one(body, body.tpe, matchPt))
+ def translateBody(body: Tree, matchPt: Type): TreeMaker =
+ BodyTreeMaker(body, matchPt)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// helper methods: they analyze types and trees in isolation, but they are not (directly) concerned with the structure of the overall translation
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
object ExtractorCall {
def apply(unfun: Tree, args: List[Tree]): ExtractorCall = new ExtractorCallRegular(unfun, args)
@@ -643,7 +645,7 @@ defined class Foo */
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// the making of the trees
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
trait TreeMakers {
@@ -657,13 +659,12 @@ defined class Foo */
protected def localSubstitution: Substitution
- private[TreeMakers] def incorporateOuterSubstitution(outerSubst: Substitution): TreeMaker = {
+ private[TreeMakers] def incorporateOuterSubstitution(outerSubst: Substitution): Unit = {
if (currSub ne null) {
println("BUG: incorporateOuterSubstitution called more than once for "+ (this, currSub, outerSubst))
Thread.dumpStack()
}
else currSub = outerSubst >> substitution
- this
}
private[this] var currSub: Substitution = null
@@ -672,6 +673,17 @@ defined class Foo */
def treesToHoist: List[Tree] = Nil
}
+ case class TrivialTreeMaker(tree: Tree) extends TreeMaker {
+ val localSubstitution: Substitution = EmptySubstitution
+ def chainBefore(next: Tree, pt: Type): Tree = tree
+ }
+
+ case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker {
+ val localSubstitution: Substitution = EmptySubstitution
+ def chainBefore(next: Tree, pt: Type): Tree = // assert(next eq EmptyTree)
+ atPos(body.pos)(substitution(pmgen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here
+ }
+
case class SubstOnlyTreeMaker(localSubstitution: Substitution) extends TreeMaker {
def chainBefore(next: Tree, pt: Type): Tree = substitution(next)
}
@@ -680,7 +692,7 @@ defined class Foo */
val nextBinder: Symbol
// for CSE (used iff optimizingCodeGen)
- // TODO: factor this out into a separate TreeMaker that gets created when reuse is detected -- don't mutate treemakers
+ // TODO: factor this out -- don't mutate treemakers
var reused: Boolean = false
def reusedBinders: List[Symbol] = Nil
override def treesToHoist: List[Tree] = { import CODE._
@@ -696,7 +708,7 @@ defined class Foo */
lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder)))
}
- // TODO: in the process of shifting optimized code gen into the treemakers: complete and make it conditional in the same way as is happening in pmgen
+ // TODO: factor out optimization-specific stuff into codegen
abstract class CondTreeMaker extends FreshFunTreeMaker { import CODE._
val cond: Tree
val res: Tree
@@ -796,39 +808,48 @@ defined class Foo */
override def toString = "ET"+(prevBinder, patTree)
}
- case class AlternativesTreeMaker(prevBinder: Symbol, altss: List[List[TreeMaker]], pos: Position) extends FreshFunTreeMaker {
- val nextBinderTp: Type = prevBinder.info.widen
- private def inlineNext(next: Tree) = {
- var okToInline = true
- var sizeBudget = 20 // yep, totally arbitrary!
- object travOkToInline extends Traverser { override def traverse(tree: Tree): Unit = if (sizeBudget >= 0) { sizeBudget -= 1; tree match {
- case TypeApply(_, _) | Apply(_, _) | Select(_, _)
- | Block(_, _) | Assign(_, _) | If(_, _, _) | Typed(_, _) => super.traverse(tree) // these are allowed if their subtrees are
- case EmptyTree | This(_) | New(_) | Literal(_) | Ident(_) => // these are always ok
- case _ if tree.isType => // these are always ok
- case _ => okToInline = false //; println("not inlining: "+ (tree, tree.getClass))
- }}}
- travOkToInline.traverse(next)
- // println("(okToInline, sizeBudget): "+ (okToInline, sizeBudget))
- okToInline && sizeBudget > 0 // must be strict comparison
+ case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker {
+ // don't substitute prevBinder to nextBinder, a set of alternatives does not need to introduce a new binder, simply reuse the previous one
+ val localSubstitution: Substitution = EmptySubstitution
+
+ override private[TreeMakers] def incorporateOuterSubstitution(outerSubst: Substitution): Unit = {
+ super.incorporateOuterSubstitution(outerSubst)
+ altss = altss map (alts => propagateSubstitution(alts, substitution))
}
+
def chainBefore(next: Tree, pt: Type): Tree = { import CODE._
+ // next does not contain deftrees, is pretty short
+ val canDuplicate = {
+ var okToInline = true
+ var sizeBudget = 100 / (altss.length max 1) // yep, totally arbitrary!
+ object travOkToInline extends Traverser { override def traverse(tree: Tree): Unit = if (sizeBudget >= 0) { sizeBudget -= 1; tree match {
+ case TypeApply(_, _) | Apply(_, _) | Select(_, _)
+ | Block(_, _) | Assign(_, _) | If(_, _, _) | Typed(_, _) => super.traverse(tree) // these are allowed if their subtrees are
+ case EmptyTree | This(_) | New(_) | Literal(_) | Ident(_) => // these are always ok
+ case _ if tree.isType => // these are always ok
+ case _ => okToInline = false //; println("not inlining: "+ (tree, tree.getClass))
+ }}}
+ travOkToInline.traverse(next)
+ // println("(okToInline, sizeBudget): "+ (okToInline, sizeBudget))
+ okToInline && sizeBudget > 0 // must be strict comparison
+ }
+
atPos(pos)(
- if (inlineNext(next)) {
- altss map (altTreeMakers =>
- combineExtractors(propagateSubstitution(altTreeMakers), next.duplicate, pt) // don't substitute prevBinder to nextBinder, beta-reduce application to prevBinder
- ) reduceLeft pmgen.typedOrElse(pt)
+ if (canDuplicate) {
+ altss map {altTreeMakers =>
+ combineExtractors(altTreeMakers :+ TrivialTreeMaker(substitution(next).duplicate), pt)
+ } reduceLeft pmgen.typedOrElse(pt)
} else {
- val rest = freshSym(pos, functionType(List(nextBinderTp), inMatchMonad(pt)), "rest")
+ val rest = freshSym(pos, functionType(List(), inMatchMonad(pt)), "rest")
// rest.info.member(nme.apply).withAnnotation(AnnotationInfo(ScalaInlineClass.tpe, Nil, Nil))
// one alternative may still generate multiple trees (e.g., an extractor call + equality test)
// (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers
val combinedAlts = altss map (altTreeMakers =>
- combineExtractors(propagateSubstitution(altTreeMakers), REF(rest) APPLY (REF(prevBinder)), pt)
+ combineExtractors(altTreeMakers :+ TrivialTreeMaker(REF(rest) APPLY ()), pt)
)
BLOCK(
- VAL(rest) === pmgen.fun(nextBinder, substitution(next)),
+ VAL(rest) === Function(Nil, substitution(next)),
combinedAlts reduceLeft pmgen.typedOrElse(pt)
)
}
@@ -838,7 +859,7 @@ defined class Foo */
case class GuardTreeMaker(guardTree: Tree) extends TreeMaker {
val localSubstitution: Substitution = EmptySubstitution
- def chainBefore(next: Tree, pt: Type): Tree = pmgen.flatMapGuard(guardTree, next)
+ def chainBefore(next: Tree, pt: Type): Tree = pmgen.flatMapGuard(substitution(guardTree), next)
override def toString = "G("+ guardTree +")"
}
@@ -947,7 +968,7 @@ defined class Foo */
*
* intended to be generalised to exhaustivity/reachability checking
*/
- def doCSE(prevBinder: Symbol, cases: List[(List[TreeMaker], Tree)], pt: Type): List[(List[TreeMaker], Tree)] = {
+ def doCSE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = {
// a variable in this set should never be replaced by a tree that "does not consist of a selection on a variable in this set" (intuitively)
val pointsToBound = collection.mutable.HashSet(prevBinder)
@@ -1013,10 +1034,11 @@ defined class Foo */
| ProductExtractorTreeMaker(_, Some(_), _) => Havoc
case AlternativesTreeMaker(_, _, _) => Havoc // TODO: can do better here
case SubstOnlyTreeMaker(_) => Top
+ case BodyTreeMaker(_, _) => Havoc
}, tm)
}
- val testss = cases.map {case (treeMakers, _) => treeMakers map approximateTreeMaker }
+ val testss = cases.map { _ map approximateTreeMaker }
// interpret:
val dependencies = new collection.mutable.LinkedHashMap[Test, Set[Cond]]
@@ -1047,7 +1069,7 @@ defined class Foo */
// then, collapse these contiguous sequences of reusing tests
// store the result of the final test and the intermediate results in hoisted mutable variables (TODO: optimize: don't store intermediate results that aren't used)
// replace each reference to a variable originally bound by a collapsed test by a reference to the hoisted variable
- (testss, cases).zipped map { case (tests, (_, caseBody)) =>
+ testss map { tests =>
var currDeps = Set[Cond]()
val (sharedPrefix, suffix) = tests span { test =>
(test.cond eq Top) || (for(
@@ -1067,63 +1089,186 @@ defined class Foo */
yield ReusingCondTreeMaker(sharedPrefix map (t => (t.treeMaker, t.reuses map (_.treeMaker)))) :: suffix.map(_.treeMaker)
} else None
- (collapsedTreeMakers getOrElse tests.map(_.treeMaker), // sharedPrefix need not be empty (but it only contains Top-tests, which are dropped above)
- caseBody)
+ collapsedTreeMakers getOrElse tests.map(_.treeMaker) // sharedPrefix need not be empty (but it only contains Top-tests, which are dropped above)
}
}
+ // TODO: non-trivial dead-code elimination
+ // e.g., the following match should compile to a simple instanceof:
+ // case class Ident(name: String)
+ // for (Ident(name) <- ts) println(name)
+ def doDCE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = {
+ // do minimal DCE
+ cases
+ }
- // a foldLeft to accumulate the localSubstitution left-to-right, mutating the treemakers in-place for performance
- def propagateSubstitution(treeMakers: List[TreeMaker]): List[TreeMaker] = {
- var accumSubst: Substitution = EmptySubstitution
+
+ def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker])
+
+ // a foldLeft to accumulate the localSubstitution left-to-right
+ // 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
+ def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = {
+ var accumSubst: Substitution = initial
treeMakers foreach { maker =>
maker incorporateOuterSubstitution accumSubst
accumSubst = maker.substitution
}
- treeMakers
+ removeSubstOnly(treeMakers)
}
- // calls propagateSubstitution on the treemakers
- def analyzeCases(prevBinder: Symbol, cases: List[(List[TreeMaker], Tree)], pt: Type): List[(List[TreeMaker], Tree)] = {
- cases foreach { case (pats, _) => propagateSubstitution(pats) }
- if (optimizingCodeGen) {
- doCSE(prevBinder, cases, pt)
- } else cases
- }
+ object SwitchablePattern { def unapply(pat: Tree) = pat match {
+ case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches
+ case _ => false
+ }}
+
+ // def isSwitchable(cases: List[(List[TreeMaker], Tree)]): Boolean = {
+ // def isSwitchableTreeMaker(tm: TreeMaker) = tm match {
+ // case tm@EqualityTestTreeMaker(_, SwitchablePattern(), _) => true
+ // case SubstOnlyTreeMaker(_) => true
+ // case AlternativesTreeMaker(_, altss, _) => altss forall (_.forall(isSwitchableTreeMaker))
+ // case _ => false
+ // }
+ // }
+
+ def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = if (optimizingCodeGen) {
+ def unfold(tms: List[TreeMaker], currLabel: Option[Symbol] = None, nextLabel: Option[Symbol] = None): List[CaseDef] = tms match {
+ // constant
+ case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => import CODE._
+ @inline
+ def substedBody = btm.substitution(body)
+ val labelledBody = currLabel match {
+ case None => substedBody // currLabel.isEmpty implies nextLabel.isEmpty
+ case Some(myLabel) =>
+ LabelDef(myLabel, Nil,
+ nextLabel match {
+ case None => substedBody
+ case Some(next) => ID(next) APPLY ()
+ }
+ )
+ }
+ List(CaseDef(const, EmptyTree, labelledBody))
+
+ // alternatives
+ case AlternativesTreeMaker(_, altss, _) :: bodyTm :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty)
+ val labels = altss map { alts =>
+ Some(freshSym(NoPosition, MethodType(Nil, pt), "$alt$") setFlag (METHOD | LABEL))
+ }
- def combineCases(scrut: Tree, scrutSym: Symbol, cases0: List[(List[TreeMaker], Tree)], pt: Type): Tree = {
- var toHoist = List[Tree]()
- val matcher =
- if (cases0 nonEmpty) {
- // when specified, need to propagate pt explicitly (type inferencer can't handle it)
- val optPt =
- if (isFullyDefined(pt)) inMatchMonad(pt)
- else NoType
-
- val cases = analyzeCases(scrutSym, cases0, pt)
-
- // map + foldLeft
- var combinedCases = combineExtractors(cases.head._1, cases.head._2, pt)
- cases.tail foreach { case (pats, body) =>
- combinedCases = pmgen.typedOrElse(optPt)(combinedCases, combineExtractors(pats, body, pt))
+ val caseDefs = (altss, labels, labels.tail :+ None).zipped.map { case (alts, currLabel, nextLabel) =>
+ unfold(alts :+ bodyTm, currLabel, nextLabel)
}
- toHoist = (for ((treeMakers, _) <- cases; tm <- treeMakers; hoisted <- tm.treesToHoist) yield hoisted).toList
+ if (caseDefs exists (_.isEmpty)) Nil
+ else caseDefs.flatten
+
+ case _ => Nil // failure
+ }
+
+ val caseDefs = cases map { makers =>
+ removeSubstOnly(makers) match {
+ // default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches)
+ case (btm@BodyTreeMaker(body, _)) :: Nil =>
+ List(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body)))
+ case nonTrivialMakers =>
+ unfold(nonTrivialMakers)
+ }
+ }
- pmgen.fun(scrutSym, combinedCases)
- } else pmgen.zero
+ if (caseDefs exists (_.isEmpty)) None
+ else { import CODE._
+ val matcher = BLOCK(
+ VAL(scrutSym) === scrut, // TODO: type test for switchable type if patterns allow switch but the scrutinee doesn't
+ Match(REF(scrutSym), caseDefs.flatten) // match on scrutSym, not scrut to avoid duplicating scrut
+ )
+ // matcher filter (tree => tree.tpe == null) foreach println
+ // treeBrowser browse matcher
+ Some(matcher) // set type to avoid recursion in typedMatch
+ }
+ } else None
- val expr = pmgen.runOrElse(scrut, matcher, scrutSym.info, if (isFullyDefined(pt)) pt else NoType)
- if (toHoist isEmpty) expr
- else Block(toHoist, expr)
+ def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] =
+ doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt)
+
+ // calls propagateSubstitution on the treemakers
+ def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){
+ val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
+
+ emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{
+ var toHoist = List[Tree]()
+ val matcher =
+ if (casesUnOpt nonEmpty) {
+ // when specified, need to propagate pt explicitly (type inferencer can't handle it)
+ val optPt =
+ if (isFullyDefined(pt)) inMatchMonad(pt)
+ else NoType
+
+ val cases =
+ if (optimizingCodeGen) optimizeCases(scrutSym, casesUnOpt, pt)
+ else casesUnOpt
+
+ val combinedCases =
+ cases.map(combineExtractors(_, pt)).reduceLeft(pmgen.typedOrElse(optPt))
+
+ toHoist = (for (treeMakers <- cases; tm <- treeMakers; hoisted <- tm.treesToHoist) yield hoisted).toList
+
+ pmgen.fun(scrutSym, combinedCases)
+ } else pmgen.zero
+
+
+ val expr = pmgen.runOrElse(scrut, matcher, scrutSym.info, if (isFullyDefined(pt)) pt else NoType)
+ if (toHoist isEmpty) expr
+ else Block(toHoist, expr)
+ }
}
// combineExtractors changes the current substitution's of the tree makers in `treeMakers`
// requires propagateSubstitution(treeMakers) has been called
- def combineExtractors(treeMakers: List[TreeMaker], body: Tree, pt: Type): Tree =
- treeMakers.foldRight (body) (_.chainBefore(_, pt))
+ def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree =
+ treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt))
+
+
+
+ // TODO: do this during tree construction, but that will require tracking the current owner in treemakers
+ // TODO: assign more fine-grained positions
+ // fixes symbol nesting, assigns positions
+ private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
+ currentOwner = origOwner
+
+ override def traverse(t: Tree) {
+ if (t != EmptyTree && t.pos == NoPosition) {
+ t.setPos(pos)
+ }
+ t match {
+ case Function(_, _) if t.symbol == NoSymbol =>
+ t.symbol = currentOwner.newValue(t.pos, nme.ANON_FUN_NAME).setFlag(SYNTHETIC).setInfo(NoType)
+ // println("new symbol for "+ (t, t.symbol.ownerChain))
+ case Function(_, _) if (t.symbol.owner == NoSymbol) || (t.symbol.owner == origOwner) =>
+ // println("fundef: "+ (t, t.symbol.ownerChain, currentOwner.ownerChain))
+ t.symbol.owner = currentOwner
+ case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2)
+ // println("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain))
+ if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree??
+ assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner)
+ d.symbol.lazyAccessor.owner = currentOwner
+ }
+ if(d.symbol.moduleClass ne NoSymbol)
+ d.symbol.moduleClass.owner = currentOwner
+ d.symbol.owner = currentOwner
+ // case _ if (t.symbol != NoSymbol) && (t.symbol ne null) =>
+ // println("untouched "+ (t, t.getClass, t.symbol.ownerChain, currentOwner.ownerChain))
+ case _ =>
+ }
+ super.traverse(t)
+ }
+
+ // override def apply
+ // println("before fixerupper: "+ xTree)
+ // currentRun.trackerFactory.snapshot()
+ // println("after fixerupper")
+ // currentRun.trackerFactory.snapshot()
+ }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// substitution
@@ -1169,6 +1314,7 @@ defined class Foo */
def fun(arg: Symbol, body: Tree): Tree
def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree
def zero: Tree
+ def one(res: Tree, bodyPt: Type, matchPt: Type): Tree
def condOptimized(c: Tree, then: Tree): Tree
def _equals(checker: Tree, binder: Symbol): Tree
def _asInstanceOf(b: Symbol, tp: Type): Tree
@@ -1176,6 +1322,7 @@ defined class Foo */
}
def pmgen: AbsCodeGen
+ def typed(tree: Tree, mode: Int, pt: Type): Tree // implemented in MatchTranslator
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1391,49 +1538,6 @@ defined class Foo */
}
def matchingStrategy: Tree
- def typed(tree: Tree, mode: Int, pt: Type): Tree // implemented in MatchTranslator
- }
-
-
- // TODO: do this during tree construction, but that will require tracking the current owner in treemakers
- // TODO: assign more fine-grained positions
- // fixes symbol nesting, assigns positions
- def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
- currentOwner = origOwner
-
- override def traverse(t: Tree) {
- if (t != EmptyTree && t.pos == NoPosition) {
- t.setPos(pos)
- }
- t match {
- case Function(_, _) if t.symbol == NoSymbol =>
- t.symbol = currentOwner.newValue(t.pos, nme.ANON_FUN_NAME).setFlag(SYNTHETIC).setInfo(NoType)
- // println("new symbol for "+ (t, t.symbol.ownerChain))
- case Function(_, _) if (t.symbol.owner == NoSymbol) || (t.symbol.owner == origOwner) =>
- // println("fundef: "+ (t, t.symbol.ownerChain, currentOwner.ownerChain))
- t.symbol.owner = currentOwner
- case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2)
- // println("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain))
- if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree??
- assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner)
- d.symbol.lazyAccessor.owner = currentOwner
- }
- if(d.symbol.moduleClass ne NoSymbol)
- d.symbol.moduleClass.owner = currentOwner
-
- d.symbol.owner = currentOwner
- // case _ if (t.symbol != NoSymbol) && (t.symbol ne null) =>
- // println("untouched "+ (t, t.getClass, t.symbol.ownerChain, currentOwner.ownerChain))
- case _ =>
- }
- super.traverse(t)
- }
-
- // override def apply
- // println("before fixerupper: "+ xTree)
- // currentRun.trackerFactory.snapshot()
- // println("after fixerupper")
- // currentRun.trackerFactory.snapshot()
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 341e1bc5ea..f6f783516c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3223,9 +3223,19 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val owntype = elimAnonymousClass(owntype0)
if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype))
- val translated = (new MatchTranslator(this)).translateMatch(selector1, cases1, owntype)
-
- typed1(translated, mode, WildcardType) setType owntype // TODO: get rid of setType owntype -- it should all typecheck
+ (new MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match {
+ case Block(vd :: Nil, tree@Match(selector, cases)) =>
+ val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
+ var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt)
+ val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
+ if (needAdapt)
+ cases1 = cases1 map (adaptCase(_, owntype))
+ typed(Block(vd :: Nil, treeCopy.Match(tree, selector1, cases1) setType owntype))
+ case translated =>
+ // TODO: get rid of setType owntype -- it should all typecheck
+ // must call typed, not typed1, or we overflow the stack when emitting switches
+ typed(translated, mode, WildcardType) setType owntype
+ }
}
}
}
diff --git a/test/files/pos/virtpatmat_alts_subst.flags b/test/files/pos/virtpatmat_alts_subst.flags
new file mode 100644
index 0000000000..9769db9257
--- /dev/null
+++ b/test/files/pos/virtpatmat_alts_subst.flags
@@ -0,0 +1 @@
+ -Yvirtpatmat -Xexperimental
diff --git a/test/files/pos/virtpatmat_alts_subst.scala b/test/files/pos/virtpatmat_alts_subst.scala
new file mode 100644
index 0000000000..e27c52f9c7
--- /dev/null
+++ b/test/files/pos/virtpatmat_alts_subst.scala
@@ -0,0 +1,6 @@
+case class Foo(s: String) {
+ def appliedType(tycon: Any) =
+ tycon match {
+ case Foo(sym @ ("NothingClass" | "AnyClass")) => println(sym)
+ }
+}
diff --git a/test/files/pos/virtpatmat_binding_opt.flags b/test/files/pos/virtpatmat_binding_opt.flags
new file mode 100644
index 0000000000..9769db9257
--- /dev/null
+++ b/test/files/pos/virtpatmat_binding_opt.flags
@@ -0,0 +1 @@
+ -Yvirtpatmat -Xexperimental
diff --git a/test/files/pos/virtpatmat_binding_opt.scala b/test/files/pos/virtpatmat_binding_opt.scala
new file mode 100644
index 0000000000..962e3d7dbe
--- /dev/null
+++ b/test/files/pos/virtpatmat_binding_opt.scala
@@ -0,0 +1,11 @@
+class Test {
+ def combine = this match {
+ case that if that eq this => this // just return this
+ case that: Test2 =>
+ println(that)
+ this
+ case _ => error("meh")
+ }
+}
+
+class Test2 extends Test \ No newline at end of file
diff --git a/test/files/run/virtpatmat_literal.scala b/test/files/run/virtpatmat_literal.scala
index cb72b1d2a5..5bd6b30791 100644
--- a/test/files/run/virtpatmat_literal.scala
+++ b/test/files/run/virtpatmat_literal.scala
@@ -1,8 +1,9 @@
object Test extends App {
+ val a = 1
1 match {
case 2 => println("FAILED")
case 1 => println("OK")
- case 1 => println("FAILED")
+ case `a` => println("FAILED")
}
val one = 1