package dotty.tools package dotc package parsing import core._ import Flags._, Trees._, TypedTrees._, UntypedTrees._, Names._, StdNames._, NameOps._, Contexts._ import scala.collection.mutable.ListBuffer import util.Positions._, Symbols._, Decorators._, Flags._, Constants._ import TreeInfo._ /** Methods for building trees, used in the parser. All the trees * returned by this class must be untyped. * Note: currently unused */ class TreeBuilder(implicit ctx: Context) { import untpd._ def scalaDot(name: Name): Select = Select(new TypedSplice(tpd.Ident(defn.ScalaPackageVal.termRef)), name) def scalaAnyRefConstr = scalaDot(tpnme.AnyRef) def scalaAnyValConstr = scalaDot(tpnme.AnyVal) def scalaAnyConstr = scalaDot(tpnme.Any) def scalaUnitConstr = scalaDot(tpnme.Unit) def productConstr = scalaDot(tpnme.Product) def productConstrN(n: Int) = scalaDot(("Product" + n).toTypeName) def serializableConstr = scalaDot(tpnme.Serializable) def convertToTypeName(t: Tree): Tree = ??? private implicit val cpos = NoPosition /** Convert all occurrences of (lower-case) variables in a pattern as follows: * x becomes x @ _ * x: T becomes x @ (_: T) * Also covert all toplevel lower-case type arguments as follows: * t becomes t @ _ */ private object patvarTransformer extends TreeTransformer { override def transform(tree: Tree): Tree = tree match { case Ident(name) if isVarPattern(tree) && name != nme.WILDCARD => Bind( name, Ident(nme.WILDCARD).withPos(tree.pos.focus) ).withPos(tree.pos) case Typed(id @ Ident(name), tpt) if isVarPattern(id) && name != nme.WILDCARD => Bind( name, Typed( Ident(nme.WILDCARD).withPos(tree.pos.focus), transform(tpt) ).withPos(tree.pos.withStart(tree.pos.point)) ).withPos(tree.pos.withPoint(id.pos.point)) case Apply(fn @ Apply(_, _), args) => tree.derivedApply(transform(fn), transform(args)) case Apply(fn, args) => tree.derivedApply(fn, transform(args)) case Typed(expr, tpt) => tree.derivedTyped(transform(expr), transform(tpt)) case Bind(name, body) => tree.derivedBind(name, transform(body)) case AppliedTypeTree(tycon, args) => tree.derivedAppliedTypeTree(tycon, args map transform) case Alternative(_) | Typed(_, _) | AndTypeTree(_, _) | Annotated(_, _) => super.transform(tree) case Parens(_) => stripParens(tree) case _ => tree } } case class VariableInfo(name: Name, tree: Tree, pos: Position) /** Traverse pattern and collect all variable names with their types in buffer * The variables keep their positions; whereas the pattern is converted to be * synthetic for all nodes that contain a variable position. */ object getVars extends TreeAccumulator[ListBuffer[VariableInfo]] { def namePos(tree: Tree, name: Name): Position = if (name contains '$') tree.pos.focus else { val start = tree.pos.start val end = start + name.decode.length Position(start, end) } override def apply(buf: ListBuffer[VariableInfo], tree: Tree): ListBuffer[VariableInfo] = { def seenName(name: Name) = buf exists (_.name == name) def add(name: Name, t: Tree): ListBuffer[VariableInfo] = if (seenName(name)) buf else buf += VariableInfo(name, t, namePos(tree, name)) tree match { case Bind(nme.WILDCARD, _) => foldOver(buf, tree) case Bind(name, Typed(tree1, tpt)) if !mayBeTypePat(tpt) => apply(add(name, tpt), tree1) case Bind(name, tree1) => apply(add(name, TypeTree()), tree1) case _ => foldOver(buf, tree) } } } /** Returns list of all pattern variables, possibly with their types, * without duplicates */ private def getVariables(tree: Tree): List[VariableInfo] = getVars(new ListBuffer[VariableInfo], tree).toList def byNameApplication(tpe: Tree): Tree = AppliedTypeTree(scalaDot(tpnme.BYNAME_PARAM_CLASS), List(tpe)) def repeatedApplication(tpe: Tree): Tree = AppliedTypeTree(scalaDot(tpnme.REPEATED_PARAM_CLASS), List(tpe)) def makeTuple(trees: List[Tree])(implicit cpos: Position): Tree = { def mkPair(t1: Tree, t2: Tree) = { if (t1.isType) AppliedTypeTree(scalaDot(tpnme.Pair), List(t1, t2)) else Pair(t1, t2) } trees reduce mkPair } def stripParens(t: Tree) = t match { case Parens(t) => t case _ => t } def makeSelfDef(name: TermName, tpt: Tree): ValDef = ValDef(Modifiers(Private), name, tpt, EmptyTree()) /** If tree is a variable pattern, return its variable info. * Otherwise return none. */ private def matchVarPattern(tree: Tree): Option[VariableInfo] = { def wildType(t: Tree): Option[Tree] = t match { case Ident(x) if x.toTermName == nme.WILDCARD => Some(TypeTree()) case Typed(Ident(x), tpt) if x.toTermName == nme.WILDCARD => Some(tpt) case _ => None } tree match { case Ident(name) => Some(VariableInfo(name, TypeTree(), tree.pos)) case Bind(name, body) => wildType(body) map (x => VariableInfo(name, x, tree.pos)) case Typed(id @ Ident(name), tpt) => Some(VariableInfo(name, tpt, id.pos)) case _ => None } } /** Create tree representing (unencoded) binary operation expression or pattern. */ def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { def mkNamed(args: List[Tree]) = if (isExpr) args map { case arg @ Assign(Ident(name), rhs) => NamedArg(name, rhs).withPos(arg.pos) case arg => arg } else args val arguments = right match { case Parens(arg) => mkNamed(arg :: Nil) case _ => right :: Nil } if (isExpr) { if (isLeftAssoc(op)) { Apply(Select(stripParens(left), op.encode).withPos(opPos), arguments) } else { val x = ctx.freshName().toTermName Block( List(ValDef(Modifiers(Synthetic), x, TypeTree(), stripParens(left))), Apply(Select(stripParens(right), op.encode).withPos(opPos), List(Ident(x).withPos(left.pos)))) } } else { Apply(Ident(op.encode).withPos(opPos), stripParens(left) :: arguments) } } /** tpt. */ def SelectConstructor(tpt: Tree): Tree = Select(tpt, nme.CONSTRUCTOR) private def splitArgss(constr: Tree, outerArgss: List[List[Tree]]): (Tree, List[List[Tree]]) = constr match { case Apply(tree, args) => splitArgss(tree, args :: outerArgss) case _ => (constr, if (outerArgss.isEmpty) ListOfNil else outerArgss) } /** new tpt(argss_1)...(argss_n) * @param npos the position spanning , without any arguments */ def makeNew(parentConstr: Tree) = { val (tpt, argss) = splitArgss(parentConstr, Nil) New(tpt, argss) } /** Create positioned tree representing an object creation stats } */ def makeNew(templ: Template): Tree = { val x = tpnme.ANON_CLASS val nu = makeNew(Ident(x)) val clsDef = { implicit val cpos = NoPosition ClassDef(Modifiers(Final), x, Nil, templ) } Block(clsDef, nu) } /** Create positioned tree representing an object creation stats } * @param cpos the position of the new, focus should be the first parent's start. */ def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree]): Tree = { val newPos = Position(cpos.start, cpos.point) val clsPos = Position(cpos.point, cpos.end) if (parents.isEmpty) makeNew(List(scalaAnyRefConstr.withPos(newPos.endPos)), self, stats) else if (parents.tail.isEmpty && stats.isEmpty) makeNew(parents.head) else { val x = tpnme.ANON_CLASS val nu = makeNew(Ident(x).withPos(newPos)).withPos(newPos) val clsDef = { implicit val cpos = clsPos ClassDef(Modifiers(Final), x, Nil, Template(???, parents, self, stats)) } Block(clsDef, nu) } } /** Create a tree representing an assignment */ def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match { case Apply(fn, args) => Apply(Select(fn, nme.update), args :+ rhs) case _ => Assign(lhs, rhs) } /** A type tree corresponding to (possibly unary) intersection type def makeIntersectionTypeTree(tps: List[Tree]): Tree = if (tps.tail.isEmpty) tps.head else CompoundTypeTree(Template(tps, emptyValDef, Nil))*/ private def labelDefAndCall(lname: TermName, rhs: Tree, call: Tree) = { val ldef = DefDef(Modifiers(Label).withPos(cpos.startPos), lname, Nil, ListOfNil, TypeTree(), rhs) Block(ldef, call) } private def labelCall(lname: TermName): Apply = Apply(Ident(lname), Nil) /** Create tree representing a while loop */ def makeWhile(lname: TermName, cond: Tree, body: Tree): Tree = { val continu = labelCall(lname).withPos((cond.pos union body.pos).endPos) val rhs = { implicit val cpos = NoPosition If(cond, Block(body, continu), Literal(Constant()).withPos(continu.pos)) } labelDefAndCall(lname, rhs, continu) } /** Create tree representing a do-while loop */ def makeDoWhile(lname: TermName, body: Tree, cond: Tree): Tree = { val continu = labelCall(lname).withPos((cond.pos union body.pos).endPos) val rhs = Block(body, If(cond, continu, Literal(Constant()).withPos(continu.pos))) labelDefAndCall(lname, rhs, continu) } /** Create block of statements `stats` */ def makeBlock(stats: List[Tree]): Tree = if (stats.isEmpty) Literal(Constant()) else if (!stats.last.isTerm) Block(stats, Literal(Constant()).withPos(cpos.endPos)) else if (stats.length == 1) stats.head else Block(stats.init, stats.last) def makePatFilter(tree: Tree, condition: Tree, canDrop: Boolean): Tree = { val cases = List( CaseDef(condition, EmptyTree(), Literal(Constant(true))), CaseDef(Ident(nme.WILDCARD), EmptyTree(), Literal(Constant(false))) ) val matchTree = makeVisitor(cases, checkExhaustive = false, canDrop) locally { implicit val cpos = tree.pos Apply(Select(tree, nme.withFilter), matchTree :: Nil) } } /** Create tree for for-comprehension generator or */ def makeGenerator(pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = { val pat1 = patvarTransformer.transform(pat) if (valeq) ValEq(pat1, rhs) else ValFrom(pat1, makePatFilter(rhs, pat1, canDrop = true)) } /* def makeSyntheticTypeParam(pname: TypeName, bounds: Tree) = TypeDef(Modifiers(DEFERRED | SYNTHETIC), pname, Nil, bounds) */ abstract class Enumerator { def pos: Position } case class ValFrom(pat: Tree, rhs: Tree) extends Enumerator { val pos = cpos union pat.pos union rhs.pos } case class ValEq(pat: Tree, rhs: Tree) extends Enumerator { val pos = cpos union pat.pos union rhs.pos } case class Filter(test: Tree) extends Enumerator { val pos = cpos union test.pos } /** Create tree for for-comprehension or * where mapName and flatMapName are chosen * corresponding to whether this is a for-do or a for-yield. * The creation performs the following rewrite rules: * * 1. * * for (P <- G) E ==> G.foreach (P => E) * * Here and in the following (P => E) is interpreted as the function (P => E) * if P is a variable pattern and as the partial function { case P => E } otherwise. * * 2. * * for (P <- G) yield E ==> G.map (P => E) * * 3. * * for (P_1 <- G_1; P_2 <- G_2; ...) ... * ==> * G_1.flatMap (P_1 => for (P_2 <- G_2; ...) ...) * * 4. * * for (P <- G; E; ...) ... * => * for (P <- G.filter (P => E); ...) ... * * 5. For any N: * * for (P_1 <- G; P_2 = E_2; val P_N = E_N; ...) * ==> * for (TupleN(P_1, P_2, ... P_N) <- * for (x_1 @ P_1 <- G) yield { * val x_2 @ P_2 = E_2 * ... * val x_N & P_N = E_N * TupleN(x_1, ..., x_N) * } ...) * * If any of the P_i are variable patterns, the corresponding `x_i @ P_i' is not generated * and the variable constituting P_i is used instead of x_i * * @param mapName The name to be used for maps (either map or foreach) * @param flatMapName The name to be used for flatMaps (either flatMap or foreach) * @param enums The enumerators in the for expression * @param body The body of the for expression */ private def makeFor(mapName: TermName, flatMapName: TermName, enums: List[Enumerator], body: Tree): Tree = { /** make a closure pat => body. * The closure is assigned a transparent position with the point at pos.point and * the limits given by pat and body. */ def makeClosure(pat: Tree, body: Tree): Tree = matchVarPattern(pat) match { case Some(VariableInfo(name, tpt, pos)) => Function(ValDef(Modifiers(Param).withPos(cpos.startPos), name.toTermName, tpt, EmptyTree()).withPos(pos) :: Nil, body) case None => makeVisitor(List(CaseDef(pat, EmptyTree(), body)), checkExhaustive = false) } /** Make an application qual.meth(pat => body) positioned at `pos`. */ def makeCombination(meth: TermName, qual: Tree, pat: Tree, body: Tree): Tree = Apply(Select(qual, meth).withPos(NoPosition), makeClosure(pat, body)) /** Optionally, if pattern is a `Bind`, the bound name, otherwise None. */ def patternVar(pat: Tree): Option[Name] = pat match { case Bind(name, _) => Some(name) case _ => None } /** If `pat` is not yet a `Bind` wrap it in one with a fresh name */ def makeBind(pat: Tree): Tree = pat match { case Bind(_, _) => pat case _ => Bind(ctx.freshName().toTermName, pat) } /** A reference to the name bound in Bind `pat`. */ def makeValue(pat: Tree): Tree = pat match { case Bind(name, _) => Ident(name).withPos(pat.pos.focus) } enums match { case (enum @ ValFrom(pat, rhs)) :: Nil => makeCombination(mapName, rhs, pat, body).withPos(enum.pos) case ValFrom(pat, rhs) :: (rest @ (ValFrom( _, _) :: _)) => makeCombination(flatMapName, rhs, pat, makeFor(mapName, flatMapName, rest, body)) case (enum @ ValFrom(pat, rhs)) :: Filter(test) :: rest => makeFor(mapName, flatMapName, ValFrom(pat, makeCombination(nme.withFilter, rhs, pat, test)) :: rest, body) case (enum @ ValFrom(pat, rhs)) :: rest => val (valeqs, rest1) = rest.span(_.isInstanceOf[ValEq]) assert(!valeqs.isEmpty) val pats = valeqs map { case ValEq(pat, _) => pat } val rhss = valeqs map { case ValEq(_, rhs) => rhs } val defpat1 = makeBind(pat) val defpats = pats map makeBind val pdefs = (defpats, rhss).zipped flatMap (makePatDef) val ids = (defpat1 :: defpats) map makeValue val rhs1 = makeForYield(ValFrom(defpat1, rhs) :: Nil, Block(pdefs, makeTuple(ids))) val allpats = pat :: pats val vfrom1 = ValFrom(makeTuple(allpats), rhs1) makeFor(mapName, flatMapName, vfrom1 :: rest1, body) case _ => EmptyTree() //may happen for erroneous input } } /** Create tree for for-do comprehension */ def makeFor(enums: List[Enumerator], body: Tree): Tree = makeFor(nme.foreach, nme.foreach, enums, body) /** Create tree for for-yield comprehension */ def makeForYield(enums: List[Enumerator], body: Tree): Tree = makeFor(nme.map, nme.flatMap, enums, body) /** Create tree for a pattern alternative */ def makeAlternative(ts: List[Tree]): Tree = Alternative(ts flatMap alternatives) def alternatives(t: Tree): List[Tree] = t match { case Alternative(ts) => ts case _ => List(t) } def mkAnnotated(cls: Symbol, tree: Tree) = Annotated(TypedSplice(tpd.New(cls.typeRef)), tree) /** Create visitor x match cases> */ def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean, canDrop: Boolean = false): Tree = { val x = ctx.freshName().toTermName val id = Ident(x) val sel = if (canDrop) mkAnnotated(???, id) else if (!checkExhaustive) mkAnnotated(defn.UncheckedAnnot, id) else id Function(List(ugen.syntheticParameter(x)), Match(sel, cases)) } /** Create tree for case definition rhs> */ def makeCaseDef(pat: Tree, guard: Tree, rhs: Tree): CaseDef = CaseDef(patvarTransformer.transform(pat), guard, rhs) /** Create tree for pattern definition */ def makePatDef(pat: Tree, rhs: Tree): List[Tree] = makePatDef(Modifiers(), pat, rhs) /** Create tree for pattern definition */ def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree, varsArePatterns: Boolean = false): List[Tree] = matchVarPattern(pat) match { case Some(VariableInfo(name, tpt, pos)) if varsArePatterns => ValDef(mods, name.toTermName, tpt, rhs).withPos(pos) :: Nil // point comes from pat.pos case _ => // in case there is exactly one variable x_1 in pattern // val/var p = e ==> val/var x_1 = e.match (case p => (x_1)) // // in case there are zero or more than one variables in pattern // val/var p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) // val/var x_1 = t$._1 // ... // val/var x_N = t$._N val rhsUnchecked = mkAnnotated(defn.UncheckedAnnot, rhs) // TODO: clean this up -- there is too much information packed into makePatDef's `pat` argument // when it's a simple identifier (case Some((name, tpt)) -- above), // pat should have the type ascription that was specified by the user // however, in `case None` (here), we must be careful not to generate illegal pattern trees (such as `(a, b): Tuple2[Int, String]`) // i.e., this must hold: pat1 match { case Typed(expr, tp) => assert(expr.isInstanceOf[Ident]) case _ => } // if we encounter such an erroneous pattern, we strip off the type ascription from pat and propagate the type information to rhs val (pat1, rhs1) = patvarTransformer.transform(pat) match { // move the Typed ascription to the rhs case Typed(expr, tpt) if !expr.isInstanceOf[Ident] => val rhsTypedUnchecked = if (tpt.isEmpty) rhsUnchecked else Typed(rhsUnchecked, tpt) (expr, rhsTypedUnchecked) case ok => (ok, rhsUnchecked) } val vars = getVariables(pat1) val ids = vars map (v => Ident(v.name).withPos(v.pos)) val caseDef = CaseDef(pat1, EmptyTree(), makeTuple(ids)) val matchExpr = Match(rhs1, caseDef :: Nil) vars match { case List(VariableInfo(vname, tpt, pos)) => ValDef(mods, vname.toTermName, tpt, matchExpr) :: Nil case _ => val tmpName = ctx.freshName().toTermName val patMods = Modifiers(PrivateLocal | Synthetic | (mods.flags & Lazy)) val firstDef = ValDef(patMods, tmpName, TypeTree(), matchExpr) val restDefs = for { (VariableInfo(vname, tpt, pos), n) <- vars.zipWithIndex } yield { val rhs = { implicit val cpos = pos.focus Select(Ident(tmpName), ("_" + n).toTermName) } ValDef(mods, vname.toTermName, tpt, rhs).withPos(pos) } firstDef :: restDefs } } /** Create a tree representing the function type (argtpes) => restpe */ def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = AppliedTypeTree(scalaDot(("Function" + argtpes.length).toTypeName), argtpes ::: List(restpe)) /** Append implicit parameter section if `contextBounds` nonempty */ def addEvidenceParams(owner: Name, vparamss: List[List[ValDef]], contextBounds: List[Tree]): List[List[ValDef]] = { if (contextBounds.isEmpty) vparamss else { val mods = Modifiers(if (owner.isTypeName) PrivateLocal | ParamAccessor else Param) val evidenceParams = for (tpt <- contextBounds) yield { val pname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName ValDef(mods | Implicit | Synthetic, pname, tpt, EmptyTree()) } vparamss.reverse match { case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit => vparamss.init :+ (evidenceParams ++ vparams) case _ => vparamss :+ evidenceParams } } } }