factoring more into ProtoTreeMakers
contemplating the demise of ProtoTreeMaker, could TreeMaker be all we need? no review, but with apologies if this generates merge conflicts for those exhausting themselves git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@26010 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
parent
e8e8179dad
commit
dacb603b75
|
|
@ -32,7 +32,6 @@ import Flags.{ CASE => _, _ }
|
|||
d => body)))))(scrut)
|
||||
|
||||
TODO:
|
||||
- check test suite (pos/t602.scala, pos/t3856.scala, jvm/t3412, jvm/t3412-channel, ...)
|
||||
- optimizer loops on virtpatmat compiler?
|
||||
|
||||
- don't orElse a failure case at the end if there's a default case
|
||||
|
|
@ -93,7 +92,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
case Match(scrut, cases) =>
|
||||
val scrutType = if(scrut.tpe ne null) repeatedToSeq(elimAnonymousClass(scrut.tpe.widen)) else {error("TODO: support match with empty scrut"); NoType} // TODO: ErrorTree
|
||||
val scrutSym = freshSym(tree.pos, scrutType)
|
||||
pmgen.matchFromCases(scrut, scrutSym, (cases map translateCase(scrutSym)) ++ List(pmgen.zero), pt)
|
||||
matchFromCases(scrut, scrutSym, (cases map translateCase(scrutSym)) ++ List(pmgen.zero), pt)
|
||||
case t => t
|
||||
}
|
||||
|
||||
|
|
@ -219,13 +218,13 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
// val cond = genTypeDirectedEquals(prevBinder, prevBinder.info.widen, extractor.paramType) -- this seems to slow down compilation A LOT
|
||||
// chain a cast before the actual extractor call
|
||||
// need to substitute since binder may be used outside of the next extractor call (say, in the body of the case)
|
||||
(
|
||||
List(ProtoTreeMaker(List(pmgen.condCast(cond, prevBinder, extractor.paramType)), { outerSubst: TreeSubst =>
|
||||
val theSubst = typedSubst(List(prevBinder), List(CODE.REF(castedBinder)))
|
||||
def nextSubst(tree: Tree): Tree = outerSubst(theSubst(tree))
|
||||
(nestedTree => pmgen.fun(castedBinder, nextSubst(nestedTree)), nextSubst)})),
|
||||
castedBinder
|
||||
)
|
||||
val typeTestProtoTreeMaker = ProtoTreeMaker(
|
||||
List(pmgen.condCast(cond, prevBinder, extractor.paramType)),
|
||||
castedBinder,
|
||||
List(prevBinder),
|
||||
List(CODE.REF(castedBinder)))
|
||||
|
||||
(List(typeTestProtoTreeMaker), castedBinder)
|
||||
} else (Nil, prevBinder)
|
||||
|
||||
// the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern)
|
||||
|
|
@ -237,19 +236,9 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
|
||||
// println("patTreeLifted= "+ patTreeLifted)
|
||||
|
||||
val extractorProtoTreeMaker = ProtoTreeMaker(List(patTreeLifted),
|
||||
if(patBinders isEmpty)
|
||||
{ outerSubst: TreeSubst =>
|
||||
val binder = freshSym(pos, extractor.resultInMonad) // UnitClass.tpe is definitely wrong when extractor.isSeq, and extractor.resultInMonad should always be correct since it comes directly from the extractor's result type
|
||||
(nestedTree => pmgen.fun(binder, extractor.lengthGuard(binder, outerSubst(nestedTree))), outerSubst)
|
||||
}
|
||||
else
|
||||
{ outerSubst: TreeSubst =>
|
||||
val binder = freshSym(pos, extractor.resultInMonad)
|
||||
val theSubst = typedSubst(patBinders, extractor.subPatRefs(binder))
|
||||
def nextSubst(tree: Tree): Tree = outerSubst(theSubst(tree))
|
||||
(nestedTree => pmgen.fun(binder, extractor.lengthGuard(binder, nextSubst(nestedTree))), nextSubst)
|
||||
})
|
||||
val binder = freshSym(pos, extractor.resultInMonad) // can't simplify this when patBinders.isEmpty, since UnitClass.tpe is definitely wrong when extractor.isSeq, and extractor.resultInMonad should always be correct since it comes directly from the extractor's result type
|
||||
val patRefs = if(patBinders isEmpty) Nil else extractor.subPatRefs(binder)
|
||||
val extractorProtoTreeMaker = ProtoTreeMaker(List(patTreeLifted), binder, patBinders, patRefs, extractor.lengthGuard(binder))
|
||||
|
||||
withSubPats(typeTestProtoTreeMaker :+ extractorProtoTreeMaker, sub.zip: _*)
|
||||
}
|
||||
|
|
@ -328,12 +317,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
case BoundSym(patBinder, p) =>
|
||||
// TreeMaker with empty list of trees only performs the substitution patBinder --> prevBinder
|
||||
// println("rebind "+ patBinder +" to "+ prevBinder)
|
||||
withSubPats(List(ProtoTreeMaker(List(), { outerSubst: TreeSubst =>
|
||||
val theSubst = typedSubst(List(patBinder), List(CODE.REF(prevBinder)))
|
||||
// println("proto subst of: "+ patBinder)
|
||||
def nextSubst(tree: Tree): Tree = outerSubst(theSubst(tree))
|
||||
(nestedTree => nextSubst(nestedTree), nextSubst)
|
||||
})),
|
||||
withSubPats(List(ProtoTreeMaker.substOnly(List(patBinder), List(CODE.REF(prevBinder)))),
|
||||
// the symbols are markers that may be used to refer to the result of the extractor in which the corresponding tree is nested
|
||||
// it's the responsibility of the proto treemaker to replace this symbol by a reference that
|
||||
// selects that result on the function symbol of the flatMap call that binds to the result of this extractor
|
||||
|
|
@ -399,12 +383,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
|
||||
def translateGuard(guard: Tree): List[ProtoTreeMaker] = {
|
||||
if (guard == EmptyTree) List()
|
||||
else List(
|
||||
ProtoTreeMaker(List(pmgen.guard(guard)),
|
||||
{ outerSubst =>
|
||||
val binder = freshSym(guard.pos, UnitClass.tpe)
|
||||
(nestedTree => pmgen.fun(binder, outerSubst(nestedTree)), outerSubst) // guard does not bind any variables, so next subst is the current one
|
||||
}))
|
||||
else {
|
||||
val binder = freshSym(guard.pos, UnitClass.tpe)
|
||||
List(ProtoTreeMaker(List(pmgen.guard(guard)), binder))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -548,7 +530,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
else ((1 to nbSubPats) map pmgen.tupleSel(binder))).toList
|
||||
}
|
||||
|
||||
def lengthGuard(binder: Symbol, then: Tree) =
|
||||
def lengthGuard(binder: Symbol)(then: Tree) =
|
||||
// no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied
|
||||
if (!isSeq || (expectedLength < minLenToCheck)) then
|
||||
else { import CODE._
|
||||
|
|
@ -680,13 +662,18 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
|
||||
// the intermediate language -- can we make this rich enough to do analyses on (exhaustivity/reachability), without looking at the concrete trees?
|
||||
trait PatternLanguage {
|
||||
def matchingMonadType: Type
|
||||
|
||||
def typedSubst(from: List[Symbol], to: List[Tree]): TreeSubst
|
||||
def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x"): Symbol
|
||||
|
||||
// codegen relevant to the structure of the translation (how extractors are combined)
|
||||
trait AbsCodeGen {
|
||||
def runOrElse(scrut: Tree, matcher: Tree): Tree
|
||||
def flatMap(a: Tree, b: Tree): Tree
|
||||
def fun(arg: Symbol, body: Tree): Tree
|
||||
def or(f: Tree, as: List[Tree]): Tree
|
||||
def flatMap(a: Tree, b: Tree): Tree
|
||||
def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree
|
||||
}
|
||||
def pmgen: AbsCodeGen
|
||||
|
||||
|
|
@ -694,12 +681,48 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
type TreeSubst = Tree => Tree
|
||||
type TreeXForm = Tree => Tree
|
||||
|
||||
object ProtoTreeMaker {
|
||||
/**
|
||||
* Make a ProtoTreeMaker that will result in an extractor call specified by `patTrees` (see TreeMaker),
|
||||
* the next ProtoTreeMaker (here, we don't know which it'll be) is chained after this one by flatMap'ing
|
||||
* a function with binder `funBinder` over our extractor's result
|
||||
* the function's body is determined by the next ProtoTreeMaker, but it can be transformed by the current proto-treemaker (specified by `xform`)
|
||||
* in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to`
|
||||
*/
|
||||
def apply(patTrees: List[Tree], funBinder: Symbol, from: List[Symbol] = Nil, to: List[Tree] = Nil, xform: TreeXForm = identity[Tree]) = {
|
||||
if(from isEmpty)
|
||||
new ProtoTreeMaker(patTrees, { outerSubst: TreeSubst =>
|
||||
(nestedTree => pmgen.fun(funBinder, outerSubst(xform(nestedTree))), outerSubst)
|
||||
})
|
||||
else
|
||||
new ProtoTreeMaker(patTrees, { outerSubst: TreeSubst =>
|
||||
def nextSubst(tree: Tree): Tree = outerSubst(typedSubst(from, to)(tree))
|
||||
(nestedTree => pmgen.fun(funBinder, nextSubst(xform(nestedTree))), nextSubst)
|
||||
})
|
||||
}
|
||||
|
||||
def singleBinder(binderToSubst: Symbol, patTrees: Tree*): ProtoTreeMaker =
|
||||
singleBinderWithTp(binderToSubst, binderToSubst.info.widen, patTrees : _*)
|
||||
|
||||
def singleBinderWithTp(binderToSubst: Symbol, binderType: Type, patTrees: Tree*): ProtoTreeMaker = { // assert(patTrees.head.pos != NoPosition, "proto-tree for "+(binderToSubst, patTrees.toList))
|
||||
val binder = freshSym(patTrees.head.pos, binderType)
|
||||
ProtoTreeMaker(patTrees.toList, binder, List(binderToSubst), List(CODE.REF(binder)))
|
||||
}
|
||||
|
||||
def substOnly(from: List[Symbol], to: List[Tree]) = new ProtoTreeMaker(List(), { outerSubst: TreeSubst =>
|
||||
def nextSubst(tree: Tree): Tree = outerSubst(typedSubst(from, to)(tree))
|
||||
(nestedTree => nextSubst(nestedTree), nextSubst)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* substTreeMaker: takes a subst and returns the following pair:
|
||||
* - a transform that wraps a one-argument Function around a tree
|
||||
* and that replaces the binders that referred to subpatterns in that tree
|
||||
* by the corresponding selection on the function's argument (a tuple selection, a seq-index, or a seq-drop)
|
||||
* - the substitution to be applied by the next proto-tree maker
|
||||
*
|
||||
* TODO: can we get rid of ProtoTreeMaker, and just have TreeMaker?
|
||||
*/
|
||||
case class ProtoTreeMaker(extractors: List[Tree], substTreeMaker: TreeSubst => (TreeXForm, TreeSubst)) {
|
||||
def threadSubst(subst: TreeSubst): (TreeMaker, TreeSubst) = {
|
||||
|
|
@ -708,23 +731,28 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
}
|
||||
}
|
||||
|
||||
object ProtoTreeMaker {
|
||||
def singleBinder(binderToSubst: Symbol, patTrees: Tree*): ProtoTreeMaker = singleBinderWithTp(binderToSubst, binderToSubst.info.widen, patTrees : _*)
|
||||
def singleBinderWithTp(binderToSubst: Symbol, binderType: Type, patTrees: Tree*): ProtoTreeMaker = {
|
||||
assert(patTrees.head.pos != NoPosition, "proto-tree for "+(binderToSubst, patTrees.toList))
|
||||
|
||||
ProtoTreeMaker(patTrees.toList,
|
||||
{ outerSubst: TreeSubst =>
|
||||
val binder = freshSym(patTrees.head.pos, binderType)
|
||||
val theSubst = typedSubst(List(binderToSubst), List(CODE.REF(binder)))
|
||||
// println("theSubst: "+ theSubst)
|
||||
def nextSubst(tree: Tree): Tree = outerSubst(theSubst(tree))
|
||||
(nestedTree => pmgen.fun(binder, nextSubst(nestedTree)), nextSubst)
|
||||
})
|
||||
// (o => (o(foo), newO)) :: (o => (o(foo), newO')) :: (o => (o(foo), newO'')) :: (o => (o(foo), newO'''))
|
||||
// (identity(foo), newO) :: (newO(foo), newO') :: (newO'(foo), newO'') :: (newO''(foo), newO''')
|
||||
def makeTreeMakers(protoTreeMakers: List[ProtoTreeMaker]): List[TreeMaker] = {
|
||||
// run the state monad (subst is the state) and get out a list of TreeMakers
|
||||
val (treeMakers, subst) = protoTreeMakers.foldLeft((List[TreeMaker](), identity[Tree](_))){
|
||||
case ((accumTreeMakers, accumSubst), protoTreeMaker) =>
|
||||
val (treeMaker, newSubst) = protoTreeMaker threadSubst accumSubst
|
||||
(treeMaker :: accumTreeMakers, newSubst)
|
||||
}
|
||||
|
||||
treeMakers.reverse
|
||||
}
|
||||
|
||||
object TreeMaker {
|
||||
/**
|
||||
* Construct a TreeMaker given the trees that represent the extractor call
|
||||
* and the transformation to be transformed on the trees of the sub-patterns
|
||||
* meaning of `trees`:
|
||||
* - none: only doing substitution,
|
||||
* - one: a regular extractor call,
|
||||
* - many: alternatives to be fused by MatchingStrategy.or
|
||||
*/
|
||||
def apply(trees: List[Tree], genFunAndSubst0: TreeXForm): TreeMaker = trees match {
|
||||
case Nil => new NoTreeMaker{def genFunAndSubst(next: Tree) = genFunAndSubst0(next)}
|
||||
case List(tree) => new SingleTreeMaker(tree){def genFunAndSubst(next: Tree) = genFunAndSubst0(next)}
|
||||
|
|
@ -734,6 +762,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
def combine(treeMakers: List[TreeMaker], body: Tree, pos: Position) =
|
||||
atPos(pos)(treeMakers.foldRight (body) (_ genFlatMap _))
|
||||
}
|
||||
|
||||
abstract class TreeMaker {
|
||||
// wrap a Fun (with binder x) around the next tree and do aggregated substitution (which
|
||||
// replaces old pattern bindings by the appropriate tuple element selection on the new binders,
|
||||
|
|
@ -756,24 +785,16 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
def genFlatMap(tree: Tree) = pmgen.or(genFunAndSubst(tree), alts) setPos alts.head.pos
|
||||
}
|
||||
|
||||
// (o => (o(foo), newO)) :: (o => (o(foo), newO')) :: (o => (o(foo), newO'')) :: (o => (o(foo), newO'''))
|
||||
// (identity(foo), newO) :: (newO(foo), newO') :: (newO'(foo), newO'') :: (newO''(foo), newO''')
|
||||
def makeTreeMakers(protoTreeMakers: List[ProtoTreeMaker]): List[TreeMaker] = {
|
||||
// run the state monad (subst is the state) and get out a list of TreeMakers
|
||||
val (treeMakers, subst) = protoTreeMakers.foldLeft((List[TreeMaker](), identity[Tree](_))){
|
||||
case ((accumTreeMakers, accumSubst), protoTreeMaker) =>
|
||||
val (treeMaker, newSubst) = protoTreeMaker threadSubst accumSubst
|
||||
(treeMaker :: accumTreeMakers, newSubst)
|
||||
}
|
||||
|
||||
treeMakers.reverse
|
||||
def matchFromCases(scrut: Tree, scrutSym: Symbol, cases: List[Tree], pt: Type): Tree = {
|
||||
// when specified, need to propagate pt explicitly, type inferencer can't handle it
|
||||
val optPt = if(!isFullyDefined(pt)) NoType else appliedType(matchingMonadType, List(pt))
|
||||
pmgen.runOrElse(scrut, pmgen.fun(scrutSym, cases reduceLeft pmgen.typedOrElse(optPt)))
|
||||
}
|
||||
}
|
||||
|
||||
// generate actual trees
|
||||
trait MatchCodeGen extends PatternLanguage {
|
||||
def matchingStrategy: Tree
|
||||
def matchingMonadType: Type
|
||||
|
||||
lazy val pmgen: CommonCodeGen with MatchingStrategyGen with MonadInstGen =
|
||||
if (matchingMonadType.typeSymbol eq OptionClass) (new CommonCodeGen with MatchingStrategyGenOpt with MonadInstGenOpt {})
|
||||
|
|
@ -823,12 +844,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
|
|||
// TODO: optimize to if (!needsTypeTest(b.info.widen, repackExistential(tp))) REF(b) else ...
|
||||
def _asInstanceOf(b: Symbol, tp: Type): Tree = gen.mkAsInstanceOf(REF(b), repackExistential(tp), true, false)
|
||||
def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false)
|
||||
|
||||
def matchFromCases(scrut: Tree, scrutSym: Symbol, cases: List[Tree], pt: Type): Tree = {
|
||||
// when specified, need to propagate pt explicitly, type inferencer can't handle it
|
||||
val optPt = if(!isFullyDefined(pt)) NoType else appliedType(matchingMonadType, List(pt))
|
||||
runOrElse(scrut, fun(scrutSym, cases reduceLeft typedOrElse(optPt)))
|
||||
}
|
||||
}
|
||||
|
||||
trait MatchingStrategyGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen =>
|
||||
|
|
|
|||
Loading…
Reference in New Issue