aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/ast
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/ast
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/ast')
-rw-r--r--compiler/src/dotty/tools/dotc/ast/CheckTrees.scala.disabled258
-rw-r--r--compiler/src/dotty/tools/dotc/ast/Desugar.scala1089
-rw-r--r--compiler/src/dotty/tools/dotc/ast/NavigateAST.scala82
-rw-r--r--compiler/src/dotty/tools/dotc/ast/PluggableTransformers.scala105
-rw-r--r--compiler/src/dotty/tools/dotc/ast/Positioned.scala213
-rw-r--r--compiler/src/dotty/tools/dotc/ast/TreeInfo.scala733
-rw-r--r--compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala187
-rw-r--r--compiler/src/dotty/tools/dotc/ast/Trees.scala1295
-rw-r--r--compiler/src/dotty/tools/dotc/ast/tpd.scala952
-rw-r--r--compiler/src/dotty/tools/dotc/ast/untpd.scala562
10 files changed, 5476 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/CheckTrees.scala.disabled b/compiler/src/dotty/tools/dotc/ast/CheckTrees.scala.disabled
new file mode 100644
index 000000000..255619f35
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/CheckTrees.scala.disabled
@@ -0,0 +1,258 @@
+package dotty.tools
+package dotc
+package ast
+
+import core._
+import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._
+import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
+
+// TODO: revise, integrate in a checking phase.
+object CheckTrees {
+
+ import tpd._
+
+ def check(p: Boolean, msg: => String = "")(implicit ctx: Context): Unit = assert(p, msg)
+
+ def checkTypeArg(arg: Tree, bounds: TypeBounds)(implicit ctx: Context): Unit = {
+ check(arg.isValueType)
+ check(bounds contains arg.tpe)
+ }
+
+ def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = {
+ var hoisted: Set[Symbol] = Set()
+ lazy val locals = ctx.typeAssigner.localSyms(block.stats).toSet
+ def isLocal(sym: Symbol): Boolean =
+ (locals contains sym) && !isHoistableClass(sym)
+ def isHoistableClass(sym: Symbol) =
+ sym.isClass && {
+ (hoisted contains sym) || {
+ hoisted += sym
+ !classLeaks(sym.asClass)
+ }
+ }
+ def leakingTypes(tp: Type): collection.Set[NamedType] =
+ tp namedPartsWith (tp => isLocal(tp.symbol))
+ def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty
+ def classLeaks(sym: ClassSymbol): Boolean =
+ (ctx.owner is Method) || // can't hoist classes out of method bodies
+ (sym.info.parents exists typeLeaks) ||
+ (sym.decls.toList exists (t => typeLeaks(t.info)))
+ leakingTypes(block.tpe)
+ }
+
+ def checkType(tree: Tree)(implicit ctx: Context): Unit = tree match {
+ case Ident(name) =>
+ case Select(qualifier, name) =>
+ check(qualifier.isValue)
+ check(qualifier.tpe =:= tree.tpe.normalizedPrefix)
+ val denot = qualifier.tpe.member(name)
+ check(denot.exists)
+ check(denot.hasAltWith(_.symbol == tree.symbol))
+ case This(cls) =>
+ case Super(qual, mixin) =>
+ check(qual.isValue)
+ val cls = qual.tpe.typeSymbol
+ check(cls.isClass)
+ case Apply(fn, args) =>
+ def checkArg(arg: Tree, name: Name, formal: Type): Unit = {
+ arg match {
+ case NamedArg(argName, _) =>
+ check(argName == name)
+ case _ =>
+ check(arg.isValue)
+ }
+ check(arg.tpe <:< formal)
+ }
+ val MethodType(paramNames, paramTypes) = fn.tpe.widen // checked already at construction
+ (args, paramNames, paramTypes).zipped foreach checkArg
+ case TypeApply(fn, args) =>
+ val pt @ PolyType(_) = fn.tpe.widen // checked already at construction
+ (args, pt.instantiateBounds(args map (_.tpe))).zipped foreach checkTypeArg
+ case Literal(const: Constant) =>
+ case New(tpt) =>
+ check(tpt.isValueType)
+ val cls = tpt.tpe.typeSymbol
+ check(cls.isClass)
+ check(!(cls is AbstractOrTrait))
+ case Pair(left, right) =>
+ check(left.isValue)
+ check(right.isValue)
+ case Typed(expr, tpt) =>
+ check(tpt.isValueType)
+ expr.tpe.widen match {
+ case tp: MethodType =>
+ val cls = tpt.tpe.typeSymbol
+ check(cls.isClass)
+ check((cls is Trait) ||
+ cls.primaryConstructor.info.paramTypess.flatten.isEmpty)
+ val absMembers = tpt.tpe.abstractTermMembers
+ check(absMembers.size == 1)
+ check(tp <:< absMembers.head.info)
+ case _ =>
+ check(expr.isValueOrPattern)
+ check(expr.tpe <:< tpt.tpe.translateParameterized(defn.RepeatedParamClass, defn.SeqClass))
+ }
+ case NamedArg(name, arg) =>
+ case Assign(lhs, rhs) =>
+ check(lhs.isValue); check(rhs.isValue)
+ lhs.tpe match {
+ case ltpe: TermRef =>
+ check(ltpe.symbol is Mutable)
+ case _ =>
+ check(false)
+ }
+ check(rhs.tpe <:< lhs.tpe.widen)
+ case tree @ Block(stats, expr) =>
+ check(expr.isValue)
+ check(escapingRefs(tree).isEmpty)
+ case If(cond, thenp, elsep) =>
+ check(cond.isValue); check(thenp.isValue); check(elsep.isValue)
+ check(cond.tpe isRef defn.BooleanClass)
+ case Closure(env, meth, target) =>
+ meth.tpe.widen match {
+ case mt @ MethodType(_, paramTypes) =>
+ if (target.isEmpty) {
+ check(env.length < paramTypes.length)
+ for ((arg, formal) <- env zip paramTypes)
+ check(arg.tpe <:< formal)
+ }
+ else
+ // env is stored in class, not method
+ target.tpe match {
+ case SAMType(targetMeth) =>
+ check(mt <:< targetMeth.info)
+ }
+ }
+ case Match(selector, cases) =>
+ check(selector.isValue)
+ // are any checks that relate selector and patterns desirable?
+ case CaseDef(pat, guard, body) =>
+ check(pat.isValueOrPattern); check(guard.isValue); check(body.isValue)
+ check(guard.tpe.derivesFrom(defn.BooleanClass))
+ case Return(expr, from) =>
+ check(expr.isValue); check(from.isTerm)
+ check(from.tpe.termSymbol.isRealMethod)
+ case Try(block, handler, finalizer) =>
+ check(block.isTerm)
+ check(finalizer.isTerm)
+ check(handler.isTerm)
+ check(handler.tpe derivesFrom defn.FunctionClass(1))
+ check(handler.tpe.baseArgInfos(defn.FunctionClass(1)).head <:< defn.ThrowableType)
+ case Throw(expr) =>
+ check(expr.isValue)
+ check(expr.tpe.derivesFrom(defn.ThrowableClass))
+ case SeqLiteral(elems) =>
+ val elemtp = tree.tpe.elemType
+ for (elem <- elems) {
+ check(elem.isValue)
+ check(elem.tpe <:< elemtp)
+ }
+ case TypeTree(original) =>
+ if (!original.isEmpty) {
+ check(original.isValueType)
+ check(original.tpe == tree.tpe)
+ }
+ case SingletonTypeTree(ref) =>
+ check(ref.isValue)
+ check(ref.symbol.isStable)
+ case SelectFromTypeTree(qualifier, name) =>
+ check(qualifier.isValueType)
+ check(qualifier.tpe =:= tree.tpe.normalizedPrefix)
+ val denot = qualifier.tpe.member(name)
+ check(denot.exists)
+ check(denot.symbol == tree.symbol)
+ case AndTypeTree(left, right) =>
+ check(left.isValueType); check(right.isValueType)
+ case OrTypeTree(left, right) =>
+ check(left.isValueType); check(right.isValueType)
+ case RefinedTypeTree(tpt, refinements) =>
+ check(tpt.isValueType)
+ def checkRefinements(forbidden: Set[Symbol], rs: List[Tree]): Unit = rs match {
+ case r :: rs1 =>
+ val rsym = r.symbol
+ check(rsym.isTerm || rsym.isAbstractOrAliasType)
+ if (rsym.isAbstractType) check(tpt.tpe.member(rsym.name).exists)
+ check(rsym.info forallParts {
+ case nt: NamedType => !(forbidden contains nt.symbol)
+ case _ => true
+ })
+ checkRefinements(forbidden - rsym, rs1)
+ case nil =>
+ }
+ checkRefinements(ctx.typeAssigner.localSyms(refinements).toSet, refinements)
+ case AppliedTypeTree(tpt, args) =>
+ check(tpt.isValueType)
+ val tparams = tpt.tpe.typeParams
+ check(sameLength(tparams, args))
+ (args, tparams map (_.info.bounds)).zipped foreach checkTypeArg
+ case TypeBoundsTree(lo, hi) =>
+ check(lo.isValueType); check(hi.isValueType)
+ check(lo.tpe <:< hi.tpe)
+ case Bind(sym, body) =>
+ check(body.isValueOrPattern)
+ check(!(tree.symbol is Method))
+ body match {
+ case Ident(nme.WILDCARD) =>
+ case _ => check(body.tpe.widen =:= tree.symbol.info)
+ }
+ case Alternative(alts) =>
+ for (alt <- alts) check(alt.isValueOrPattern)
+ case UnApply(fun, implicits, args) => // todo: review
+ check(fun.isTerm)
+ for (arg <- args) check(arg.isValueOrPattern)
+ val funtpe @ MethodType(_, _) = fun.tpe.widen
+ fun.symbol.name match { // check arg arity
+ case nme.unapplySeq =>
+ // args need to be wrapped in (...: _*)
+ check(args.length == 1)
+ check(args.head.isInstanceOf[SeqLiteral])
+ case nme.unapply =>
+ val rtp = funtpe.resultType
+ if (rtp isRef defn.BooleanClass)
+ check(args.isEmpty)
+ else {
+ check(rtp isRef defn.OptionClass)
+ val normArgs = rtp.argTypesHi match {
+ case optionArg :: Nil =>
+ optionArg.argTypesHi match {
+ case Nil =>
+ optionArg :: Nil
+ case tupleArgs if defn.isTupleType(optionArg) =>
+ tupleArgs
+ }
+ case _ =>
+ check(false)
+ Nil
+ }
+ check(sameLength(normArgs, args))
+ }
+ }
+ case ValDef(mods, name, tpt, rhs) =>
+ check(!(tree.symbol is Method))
+ if (!rhs.isEmpty) {
+ check(rhs.isValue)
+ check(rhs.tpe <:< tpt.tpe)
+ }
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ check(tree.symbol is Method)
+ if (!rhs.isEmpty) {
+ check(rhs.isValue)
+ check(rhs.tpe <:< tpt.tpe)
+ }
+ case TypeDef(mods, name, tpt) =>
+ check(tpt.isInstanceOf[Template] || tpt.tpe.isInstanceOf[TypeBounds])
+ case Template(constr, parents, selfType, body) =>
+ case Import(expr, selectors) =>
+ check(expr.isValue)
+ check(expr.tpe.termSymbol.isStable)
+ case PackageDef(pid, stats) =>
+ check(pid.isTerm)
+ check(pid.symbol is Package)
+ case Annotated(annot, arg) =>
+ check(annot.isInstantiation)
+ check(annot.symbol.owner.isSubClass(defn.AnnotationClass))
+ check(arg.isValueType || arg.isValue)
+ case EmptyTree =>
+ }
+}
+
diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
new file mode 100644
index 000000000..366a0e225
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
@@ -0,0 +1,1089 @@
+package dotty.tools
+package dotc
+package ast
+
+import core._
+import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
+import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
+import Decorators._
+import language.higherKinds
+import collection.mutable.ListBuffer
+import util.Property
+import reporting.diagnostic.messages._
+
+object desugar {
+ import untpd._
+
+ /** Tags a .withFilter call generated by desugaring a for expression.
+ * Such calls can alternatively be rewritten to use filter.
+ */
+ val MaybeFilter = new Property.Key[Unit]
+
+ /** Info of a variable in a pattern: The named tree and its type */
+ private type VarInfo = (NameTree, Tree)
+
+ /** Names of methods that are added unconditionally to case classes */
+ def isDesugaredCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean =
+ name == nme.isDefined ||
+ name == nme.copy ||
+ name == nme.productArity ||
+ name.isSelectorName
+
+// ----- DerivedTypeTrees -----------------------------------
+
+ class SetterParamTree extends DerivedTypeTree {
+ def derivedType(sym: Symbol)(implicit ctx: Context) = sym.info.resultType
+ }
+
+ class TypeRefTree extends DerivedTypeTree {
+ def derivedType(sym: Symbol)(implicit ctx: Context) = sym.typeRef
+ }
+
+ class DerivedFromParamTree extends DerivedTypeTree {
+
+ /** Make sure that for all enclosing module classes their companion lasses
+ * are completed. Reason: We need the constructor of such companion classes to
+ * be completed so that OriginalSymbol attachments are pushed to DerivedTypeTrees
+ * in apply/unapply methods.
+ */
+ override def ensureCompletions(implicit ctx: Context) =
+ if (!(ctx.owner is Package))
+ if (ctx.owner.isClass) {
+ ctx.owner.ensureCompleted()
+ if (ctx.owner is ModuleClass)
+ ctx.owner.linkedClass.ensureCompleted()
+ }
+ else ensureCompletions(ctx.outer)
+
+ /** Return info of original symbol, where all references to siblings of the
+ * original symbol (i.e. sibling and original symbol have the same owner)
+ * are rewired to same-named parameters or accessors in the scope enclosing
+ * the current scope. The current scope is the scope owned by the defined symbol
+ * itself, that's why we have to look one scope further out. If the resulting
+ * type is an alias type, dealias it. This is necessary because the
+ * accessor of a type parameter is a private type alias that cannot be accessed
+ * from subclasses.
+ */
+ def derivedType(sym: Symbol)(implicit ctx: Context) = {
+ val relocate = new TypeMap {
+ val originalOwner = sym.owner
+ def apply(tp: Type) = tp match {
+ case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) =>
+ val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next
+ var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol
+ if (local.exists) (defctx.owner.thisType select local).dealias
+ else throw new java.lang.Error(
+ s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}"
+ )
+ case _ =>
+ mapOver(tp)
+ }
+ }
+ relocate(sym.info)
+ }
+ }
+
+ /** A type definition copied from `tdef` with a rhs typetree derived from it */
+ def derivedTypeParam(tdef: TypeDef) =
+ cpy.TypeDef(tdef)(
+ rhs = new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef)
+
+ /** A value definition copied from `vdef` with a tpt typetree derived from it */
+ def derivedTermParam(vdef: ValDef) =
+ cpy.ValDef(vdef)(
+ tpt = new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef)
+
+// ----- Desugar methods -------------------------------------------------
+
+ /** var x: Int = expr
+ * ==>
+ * def x: Int = expr
+ * def x_=($1: <TypeTree()>): Unit = ()
+ */
+ def valDef(vdef: ValDef)(implicit ctx: Context): Tree = {
+ val ValDef(name, tpt, rhs) = vdef
+ val mods = vdef.mods
+ def setterNeeded =
+ (mods is Mutable) && ctx.owner.isClass && (!(mods is PrivateLocal) || (ctx.owner is Trait))
+ if (setterNeeded) {
+ // todo: copy of vdef as getter needed?
+ // val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ?
+ // right now vdef maps via expandedTree to a thicket which concerns itself.
+ // I don't see a problem with that but if there is one we can avoid it by making a copy here.
+ val setterParam = makeSyntheticParameter(tpt = (new SetterParamTree).watching(vdef))
+ val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
+ val setter = cpy.DefDef(vdef)(
+ name = name.setterName,
+ tparams = Nil,
+ vparamss = (setterParam :: Nil) :: Nil,
+ tpt = TypeTree(defn.UnitType),
+ rhs = setterRhs
+ ).withMods((mods | Accessor) &~ CaseAccessor) // rhs gets filled in later, when field is generated and getter has parameters
+ Thicket(vdef, setter)
+ }
+ else vdef
+ }
+
+ /** Expand context bounds to evidence params. E.g.,
+ *
+ * def f[T >: L <: H : B](params)
+ * ==>
+ * def f[T >: L <: H](params)(implicit evidence$0: B[T])
+ *
+ * Expand default arguments to default getters. E.g,
+ *
+ * def f[T: B](x: Int = 1)(y: String = x + "m") = ...
+ * ==>
+ * def f[T](x: Int)(y: String)(implicit evidence$0: B[T]) = ...
+ * def f$default$1[T] = 1
+ * def f$default$2[T](x: Int) = x + "m"
+ */
+ def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
+ val DefDef(name, tparams, vparamss, tpt, rhs) = meth
+ val mods = meth.mods
+ val epbuf = new ListBuffer[ValDef]
+ val tparams1 = tparams mapConserve {
+ case tparam @ TypeDef(_, ContextBounds(tbounds, cxbounds)) =>
+ for (cxbound <- cxbounds) {
+ val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param
+ val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName
+ epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit)
+ }
+ cpy.TypeDef(tparam)(rhs = tbounds)
+ case tparam =>
+ tparam
+ }
+
+ val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
+
+ /** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
+ def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
+ case vparams :: vparamss1 =>
+ val len = vparams.length
+ if (n >= len) vparams :: takeUpTo(vparamss1, n - len) else Nil
+ case _ =>
+ Nil
+ }
+
+ def normalizedVparamss = meth1.vparamss map (_ map (vparam =>
+ cpy.ValDef(vparam)(rhs = EmptyTree)))
+
+ def dropContextBound(tparam: TypeDef) = tparam.rhs match {
+ case ContextBounds(tbounds, _) => cpy.TypeDef(tparam)(rhs = tbounds)
+ case _ => tparam
+ }
+
+ def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match {
+ case (vparam :: vparams) :: vparamss1 =>
+ def defaultGetter: DefDef =
+ DefDef(
+ name = meth.name.defaultGetterName(n),
+ tparams = meth.tparams.map(tparam => dropContextBound(toDefParam(tparam))),
+ vparamss = takeUpTo(normalizedVparamss, n),
+ tpt = TypeTree(),
+ rhs = vparam.rhs
+ ).withMods(Modifiers(mods.flags & AccessFlags, mods.privateWithin))
+ val rest = defaultGetters(vparams :: vparamss1, n + 1)
+ if (vparam.rhs.isEmpty) rest else defaultGetter :: rest
+ case Nil :: vparamss1 =>
+ defaultGetters(vparamss1, n)
+ case nil =>
+ Nil
+ }
+
+ val defGetters = defaultGetters(vparamss, 0)
+ if (defGetters.isEmpty) meth1
+ else {
+ val meth2 = cpy.DefDef(meth1)(vparamss = normalizedVparamss)
+ .withMods(meth1.mods | DefaultParameterized)
+ Thicket(meth2 :: defGetters)
+ }
+ }
+
+ // Add all evidence parameters in `params` as implicit parameters to `meth` */
+ private def addEvidenceParams(meth: DefDef, params: List[ValDef])(implicit ctx: Context): DefDef =
+ params match {
+ case Nil =>
+ meth
+ case evidenceParams =>
+ val vparamss1 = meth.vparamss.reverse match {
+ case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods is Implicit =>
+ ((vparams ++ evidenceParams) :: rvparamss).reverse
+ case _ =>
+ meth.vparamss :+ evidenceParams
+ }
+ cpy.DefDef(meth)(vparamss = vparamss1)
+ }
+
+ /** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */
+ private def evidenceParams(meth: DefDef)(implicit ctx: Context): List[ValDef] =
+ meth.vparamss.reverse match {
+ case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit =>
+ vparams.dropWhile(!_.name.startsWith(nme.EVIDENCE_PARAM_PREFIX))
+ case _ =>
+ Nil
+ }
+
+ /** Fill in empty type bounds with Nothing/Any. Expand private local type parameters as follows:
+ *
+ * class C[v T]
+ * ==>
+ * class C { type v C$T; type v T = C$T }
+ */
+ def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = {
+ if (tdef.mods is PrivateLocalParam) {
+ val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner))
+ .withMods(tdef.mods &~ PrivateLocal | ExpandedName)
+ val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam))
+ .withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic)
+ Thicket(tparam, alias)
+ }
+ else tdef
+ }
+
+ @sharable private val synthetic = Modifiers(Synthetic)
+
+ private def toDefParam(tparam: TypeDef): TypeDef =
+ tparam.withMods(tparam.rawMods & EmptyFlags | Param)
+ private def toDefParam(vparam: ValDef): ValDef =
+ vparam.withMods(vparam.rawMods & Implicit | Param)
+
+ /** The expansion of a class definition. See inline comments for what is involved */
+ def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
+ val className = checkNotReservedName(cdef).asTypeName
+ val impl @ Template(constr0, parents, self, _) = cdef.rhs
+ val mods = cdef.mods
+ val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags)
+
+ val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match {
+ case meth: DefDef => (meth, Nil)
+ case Thicket((meth: DefDef) :: defaults) => (meth, defaults)
+ }
+
+ // The original type and value parameters in the constructor already have the flags
+ // needed to be type members (i.e. param, and possibly also private and local unless
+ // prefixed by type or val). `tparams` and `vparamss` are the type parameters that
+ // go in `constr`, the constructor after desugaring.
+
+ /** Does `tree' look like a reference to AnyVal? Temporary test before we have inline classes */
+ def isAnyVal(tree: Tree): Boolean = tree match {
+ case Ident(tpnme.AnyVal) => true
+ case Select(qual, tpnme.AnyVal) => isScala(qual)
+ case _ => false
+ }
+ def isScala(tree: Tree): Boolean = tree match {
+ case Ident(nme.scala_) => true
+ case Select(Ident(nme.ROOTPKG), nme.scala_) => true
+ case _ => false
+ }
+
+ val isCaseClass = mods.is(Case) && !mods.is(Module)
+ val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
+ // This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
+
+ val constrTparams = constr1.tparams map toDefParam
+ val constrVparamss =
+ if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty
+ if (isCaseClass)
+ ctx.error(CaseClassMissingParamList(cdef), cdef.namePos)
+ ListOfNil
+ }
+ else constr1.vparamss.nestedMap(toDefParam)
+ val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)
+
+ // Add constructor type parameters and evidence implicit parameters
+ // to auxiliary constructors
+ val normalizedBody = impl.body map {
+ case ddef: DefDef if ddef.name.isConstructorName =>
+ addEvidenceParams(
+ cpy.DefDef(ddef)(tparams = constrTparams),
+ evidenceParams(constr1).map(toDefParam))
+ case stat =>
+ stat
+ }
+
+ val derivedTparams = constrTparams map derivedTypeParam
+ val derivedVparamss = constrVparamss nestedMap derivedTermParam
+ val arity = constrVparamss.head.length
+
+ var classTycon: Tree = EmptyTree
+
+ // a reference to the class type, with all parameters given.
+ val classTypeRef/*: Tree*/ = {
+ // -language:keepUnions difference: classTypeRef needs type annotation, otherwise
+ // infers Ident | AppliedTypeTree, which
+ // renders the :\ in companions below untypable.
+ classTycon = (new TypeRefTree) withPos cdef.pos.startPos // watching is set at end of method
+ val tparams = impl.constr.tparams
+ if (tparams.isEmpty) classTycon else AppliedTypeTree(classTycon, tparams map refOfDef)
+ }
+
+ // new C[Ts](paramss)
+ lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
+
+ // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
+ // def isDefined = true
+ // def productArity = N
+ // def _1 = this.p1
+ // ...
+ // def _N = this.pN
+ // def copy(p1: T1 = p1: @uncheckedVariance, ...,
+ // pN: TN = pN: @uncheckedVariance)(moreParams) =
+ // new C[...](p1, ..., pN)(moreParams)
+ //
+ // Note: copy default parameters need @uncheckedVariance; see
+ // neg/t1843-variances.scala for a test case. The test would give
+ // two errors without @uncheckedVariance, one of them spurious.
+ val caseClassMeths =
+ if (isCaseClass) {
+ def syntheticProperty(name: TermName, rhs: Tree) =
+ DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic)
+ val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true)))
+ val caseParams = constrVparamss.head.toArray
+ val productElemMeths = for (i <- 0 until arity) yield
+ syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name))
+ def isRepeated(tree: Tree): Boolean = tree match {
+ case PostfixOp(_, nme.raw.STAR) => true
+ case ByNameTypeTree(tree1) => isRepeated(tree1)
+ case _ => false
+ }
+ val hasRepeatedParam = constrVparamss.exists(_.exists {
+ case ValDef(_, tpt, _) => isRepeated(tpt)
+ case _ => false
+ })
+
+ val copyMeths =
+ if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
+ else {
+ def copyDefault(vparam: ValDef) =
+ makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam))
+ val copyFirstParams = derivedVparamss.head.map(vparam =>
+ cpy.ValDef(vparam)(rhs = copyDefault(vparam)))
+ val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
+ cpy.ValDef(vparam)(rhs = EmptyTree))
+ DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr)
+ .withMods(synthetic) :: Nil
+ }
+ copyMeths ::: isDefinedMeth :: productElemMeths.toList
+ }
+ else Nil
+
+ def anyRef = ref(defn.AnyRefAlias.typeRef)
+ def productConstr(n: Int) = {
+ val tycon = scalaDot((tpnme.Product.toString + n).toTypeName)
+ val targs = constrVparamss.head map (_.tpt)
+ if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs)
+ }
+
+ // Case classes and case objects get a ProductN parent
+ var parents1 = parents
+ if (mods.is(Case) && arity <= Definitions.MaxTupleArity)
+ parents1 = parents1 :+ productConstr(arity)
+
+ // The thicket which is the desugared version of the companion object
+ // synthetic object C extends parentTpt { defs }
+ def companionDefs(parentTpt: Tree, defs: List[Tree]) =
+ moduleDef(
+ ModuleDef(
+ className.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
+ .withMods(companionMods | Synthetic))
+ .withPos(cdef.pos).toList
+
+ // The companion object definitions, if a companion is needed, Nil otherwise.
+ // companion definitions include:
+ // 1. If class is a case class case class C[Ts](p1: T1, ..., pN: TN)(moreParams):
+ // def apply[Ts](p1: T1, ..., pN: TN)(moreParams) = new C[Ts](p1, ..., pN)(moreParams) (unless C is abstract)
+ // def unapply[Ts]($1: C[Ts]) = $1
+ // 2. The default getters of the constructor
+ // The parent of the companion object of a non-parameterized case class
+ // (T11, ..., T1N) => ... => (TM1, ..., TMN) => C
+ // For all other classes, the parent is AnyRef.
+ val companions =
+ if (isCaseClass) {
+ val parent =
+ if (constrTparams.nonEmpty ||
+ constrVparamss.length > 1 ||
+ mods.is(Abstract) ||
+ constr.mods.is(Private)) anyRef
+ // todo: also use anyRef if constructor has a dependent method type (or rule that out)!
+ else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe))
+ val applyMeths =
+ if (mods is Abstract) Nil
+ else
+ DefDef(nme.apply, derivedTparams, derivedVparamss, TypeTree(), creatorExpr)
+ .withFlags(Synthetic | (constr1.mods.flags & DefaultParameterized)) :: Nil
+ val unapplyMeth = {
+ val unapplyParam = makeSyntheticParameter(tpt = classTypeRef)
+ val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name)
+ DefDef(nme.unapply, derivedTparams, (unapplyParam :: Nil) :: Nil, TypeTree(), unapplyRHS)
+ .withMods(synthetic)
+ }
+ companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
+ }
+ else if (defaultGetters.nonEmpty)
+ companionDefs(anyRef, defaultGetters)
+ else if (isValueClass)
+ companionDefs(anyRef, Nil)
+ else Nil
+
+
+ // For an implicit class C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, .., pMN: TMN), the method
+ // synthetic implicit C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, ..., pMN: TMN): C[Ts] =
+ // new C[Ts](p11, ..., p1N) ... (pM1, ..., pMN) =
+ val implicitWrappers =
+ if (!mods.is(Implicit))
+ Nil
+ else if (ctx.owner is Package) {
+ ctx.error(TopLevelImplicitClass(cdef), cdef.pos)
+ Nil
+ }
+ else if (isCaseClass) {
+ ctx.error(ImplicitCaseClass(cdef), cdef.pos)
+ Nil
+ }
+ else
+ // implicit wrapper is typechecked in same scope as constructor, so
+ // we can reuse the constructor parameters; no derived params are needed.
+ DefDef(className.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
+ .withMods(companionMods | Synthetic | Implicit)
+ .withPos(cdef.pos) :: Nil
+
+ val self1 = {
+ val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt
+ if (self.isEmpty) self
+ else cpy.ValDef(self)(tpt = selfType).withMods(self.mods | SelfName)
+ }
+
+ val cdef1 = {
+ val originalTparams = constr1.tparams.toIterator
+ val originalVparams = constr1.vparamss.toIterator.flatten
+ val tparamAccessors = derivedTparams.map(_.withMods(originalTparams.next.mods))
+ val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags
+ val vparamAccessors = derivedVparamss.flatten.map(_.withMods(originalVparams.next.mods | caseAccessor))
+ cpy.TypeDef(cdef)(
+ name = className,
+ rhs = cpy.Template(impl)(constr, parents1, self1,
+ tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths))
+ }
+
+ // install the watch on classTycon
+ classTycon match {
+ case tycon: DerivedTypeTree => tycon.watching(cdef1)
+ case _ =>
+ }
+
+ flatTree(cdef1 :: companions ::: implicitWrappers)
+ }
+
+ val AccessOrSynthetic = AccessFlags | Synthetic
+
+ /** Expand
+ *
+ * object name extends parents { self => body }
+ *
+ * to:
+ * <module> val name: name$ = New(name$)
+ * <module> final class name$ extends parents { self: name.type => body }
+ */
+ def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = {
+ val moduleName = checkNotReservedName(mdef).asTermName
+ val tmpl = mdef.impl
+ val mods = mdef.mods
+ if (mods is Package)
+ PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, tmpl).withMods(mods &~ Package) :: Nil)
+ else {
+ val clsName = moduleName.moduleClassName
+ val clsRef = Ident(clsName)
+ val modul = ValDef(moduleName, clsRef, New(clsRef, Nil))
+ .withMods(mods | ModuleCreationFlags | mods.flags & AccessFlags)
+ .withPos(mdef.pos)
+ val ValDef(selfName, selfTpt, _) = tmpl.self
+ val selfMods = tmpl.self.mods
+ if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos)
+ val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), tmpl.self.rhs)
+ .withMods(selfMods)
+ .withPos(tmpl.self.pos orElse tmpl.pos.startPos)
+ val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body)
+ val cls = TypeDef(clsName, clsTmpl)
+ .withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags)
+ Thicket(modul, classDef(cls).withPos(mdef.pos))
+ }
+ }
+
+ /** The name of `mdef`, after checking that it does not redefine a Scala core class.
+ * If it does redefine, issue an error and return a mangled name instead of the original one.
+ */
+ def checkNotReservedName(mdef: MemberDef)(implicit ctx: Context): Name = {
+ val name = mdef.name
+ if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
+ def kind = if (name.isTypeName) "class" else "object"
+ ctx.error(em"illegal redefinition of standard $kind $name", mdef.pos)
+ name.errorName
+ }
+ else name
+ }
+
+ /** val p1, ..., pN: T = E
+ * ==>
+ * makePatDef[[val p1: T1 = E]]; ...; makePatDef[[val pN: TN = E]]
+ */
+ def patDef(pdef: PatDef)(implicit ctx: Context): Tree = {
+ val PatDef(mods, pats, tpt, rhs) = pdef
+ val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
+ flatTree(pats1 map (makePatDef(pdef, mods, _, rhs)))
+ }
+
+ /** If `pat` is a variable pattern,
+ *
+ * val/var/lazy val p = e
+ *
+ * Otherwise, in case there is exactly one variable x_1 in pattern
+ * val/var/lazy val p = e ==> val/var/lazy val x_1 = (e: @unchecked) match (case p => (x_1))
+ *
+ * in case there are zero or more than one variables in pattern
+ * val/var/lazy p = e ==> private synthetic [lazy] val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
+ * val/var/def x_1 = t$._1
+ * ...
+ * val/var/def x_N = t$._N
+ * If the original pattern variable carries a type annotation, so does the corresponding
+ * ValDef or DefDef.
+ */
+ def makePatDef(original: Tree, mods: Modifiers, pat: Tree, rhs: Tree)(implicit ctx: Context): Tree = pat match {
+ case VarPattern(named, tpt) =>
+ derivedValDef(original, named, tpt, rhs, mods)
+ case _ =>
+ val rhsUnchecked = makeAnnotated(defn.UncheckedAnnot, rhs)
+ val vars = getVariables(pat)
+ val isMatchingTuple: Tree => Boolean = {
+ case Tuple(es) => es.length == vars.length
+ case _ => false
+ }
+ val ids = for ((named, _) <- vars) yield Ident(named.name)
+ val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
+ val matchExpr =
+ if (forallResults(rhs, isMatchingTuple)) rhs
+ else Match(rhsUnchecked, caseDef :: Nil)
+ vars match {
+ case Nil =>
+ matchExpr
+ case (named, tpt) :: Nil =>
+ derivedValDef(original, named, tpt, matchExpr, mods)
+ case _ =>
+ val tmpName = ctx.freshName().toTermName
+ val patMods = mods & (AccessFlags | Lazy) | Synthetic
+ val firstDef =
+ ValDef(tmpName, TypeTree(), matchExpr)
+ .withPos(pat.pos.union(rhs.pos)).withMods(patMods)
+ def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n))
+ val restDefs =
+ for (((named, tpt), n) <- vars.zipWithIndex)
+ yield
+ if (mods is Lazy) derivedDefDef(original, named, tpt, selector(n), mods &~ Lazy)
+ else derivedValDef(original, named, tpt, selector(n), mods)
+ flatTree(firstDef :: restDefs)
+ }
+ }
+
+ /** Expand variable identifier x to x @ _ */
+ def patternVar(tree: Tree)(implicit ctx: Context) = {
+ val Ident(name) = tree
+ Bind(name, Ident(nme.WILDCARD)).withPos(tree.pos)
+ }
+
+ def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case tree: ValDef => valDef(tree)
+ case tree: TypeDef => if (tree.isClassDef) classDef(tree) else typeDef(tree)
+ case tree: DefDef => defDef(tree)
+ case tree: ModuleDef => moduleDef(tree)
+ case tree: PatDef => patDef(tree)
+ }
+
+ /** { stats; <empty > }
+ * ==>
+ * { stats; () }
+ */
+ def block(tree: Block)(implicit ctx: Context): Block = tree.expr match {
+ case EmptyTree =>
+ cpy.Block(tree)(tree.stats,
+ unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos))
+ case _ =>
+ tree
+ }
+
+ /** EmptyTree in lower bound ==> Nothing
+ * EmptyTree in upper bounds ==> Any
+ */
+ def typeBoundsTree(tree: TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = {
+ val TypeBoundsTree(lo, hi) = tree
+ val lo1 = if (lo.isEmpty) untpd.TypeTree(defn.NothingType) else lo
+ val hi1 = if (hi.isEmpty) untpd.TypeTree(defn.AnyType) else hi
+ cpy.TypeBoundsTree(tree)(lo1, hi1)
+ }
+
+ /** Make closure corresponding to function.
+ * params => body
+ * ==>
+ * def $anonfun(params) = body
+ * Closure($anonfun)
+ *
+ * If `inlineable` is true, tag $anonfun with an @inline annotation.
+ */
+ def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = {
+ var mods = synthetic
+ if (inlineable) mods |= Inline
+ Block(
+ DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods),
+ Closure(Nil, Ident(nme.ANON_FUN), EmptyTree))
+ }
+
+ /** If `nparams` == 1, expand partial function
+ *
+ * { cases }
+ * ==>
+ * x$1 => (x$1 @unchecked) match { cases }
+ *
+ * If `nparams` != 1, expand instead to
+ *
+ * (x$1, ..., x$n) => (x$0, ..., x${n-1} @unchecked) match { cases }
+ */
+ def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1, unchecked: Boolean = true)(implicit ctx: Context) = {
+ val params = (1 to nparams).toList.map(makeSyntheticParameter(_))
+ val selector = makeTuple(params.map(p => Ident(p.name)))
+
+ if (unchecked)
+ Function(params, Match(Annotated(selector, New(ref(defn.UncheckedAnnotType))), cases))
+ else
+ Function(params, Match(selector, cases))
+ }
+
+ /** Map n-ary function `(p1, ..., pn) => body` where n != 1 to unary function as follows:
+ *
+ * x$1 => {
+ * def p1 = x$1._1
+ * ...
+ * def pn = x$1._n
+ * body
+ * }
+ */
+ def makeTupledFunction(params: List[ValDef], body: Tree)(implicit ctx: Context): Tree = {
+ val param = makeSyntheticParameter()
+ def selector(n: Int) = Select(refOfDef(param), nme.selectorName(n))
+ val vdefs =
+ params.zipWithIndex.map{
+ case (param, idx) =>
+ DefDef(param.name, Nil, Nil, TypeTree(), selector(idx)).withPos(param.pos)
+ }
+ Function(param :: Nil, Block(vdefs, body))
+ }
+
+ /** Add annotation with class `cls` to tree:
+ * tree @cls
+ */
+ def makeAnnotated(cls: Symbol, tree: Tree)(implicit ctx: Context) =
+ Annotated(tree, untpd.New(untpd.TypeTree(cls.typeRef), Nil))
+
+ private def derivedValDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(implicit ctx: Context) = {
+ val vdef = ValDef(named.name.asTermName, tpt, rhs)
+ .withMods(mods)
+ .withPos(original.pos.withPoint(named.pos.start))
+ val mayNeedSetter = valDef(vdef)
+ mayNeedSetter
+ }
+
+ private def derivedDefDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers) =
+ DefDef(named.name.asTermName, Nil, Nil, tpt, rhs)
+ .withMods(mods)
+ .withPos(original.pos.withPoint(named.pos.start))
+
+ /** Main desugaring method */
+ def apply(tree: Tree)(implicit ctx: Context): Tree = {
+
+ /** { label def lname(): Unit = rhs; call }
+ */
+ def labelDefAndCall(lname: TermName, rhs: Tree, call: Tree) = {
+ val ldef = DefDef(lname, Nil, ListOfNil, TypeTree(defn.UnitType), rhs).withFlags(Label)
+ Block(ldef, call)
+ }
+
+ /** Translate infix operation expression left op right
+ */
+ def makeBinop(left: Tree, op: Name, right: Tree): Tree = {
+ def assignToNamedArg(arg: Tree) = arg match {
+ case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs)
+ case _ => arg
+ }
+ if (isLeftAssoc(op)) {
+ val args: List[Tree] = right match {
+ case Parens(arg) => assignToNamedArg(arg) :: Nil
+ case Tuple(args) => args mapConserve assignToNamedArg
+ case _ => right :: Nil
+ }
+ Apply(Select(left, op), args)
+ } else {
+ val x = ctx.freshName().toTermName
+ new InfixOpBlock(
+ ValDef(x, TypeTree(), left).withMods(synthetic),
+ Apply(Select(right, op), Ident(x)))
+ }
+ }
+
+ /** Create tree for for-comprehension `<for (enums) do body>` or
+ * `<for (enums) yield body>` 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
+ */
+ def makeFor(mapName: TermName, flatMapName: TermName, enums: List[Tree], body: Tree): Tree = ctx.traceIndented(i"make for ${ForYield(enums, body)}", show = true) {
+
+ /** Make a function value pat => body.
+ * If pat is a var pattern id: T then this gives (id: T) => body
+ * Otherwise this gives { case pat => body }
+ */
+ def makeLambda(pat: Tree, body: Tree): Tree = pat match {
+ case VarPattern(named, tpt) =>
+ Function(derivedValDef(pat, named, tpt, EmptyTree, Modifiers(Param)) :: Nil, body)
+ case _ =>
+ makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, unchecked = false)
+ }
+
+ /** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap
+ * it in a Bind with a fresh name. Return the transformed pattern, and the identifier
+ * that refers to the bound variable for the pattern.
+ */
+ def makeIdPat(pat: Tree): (Tree, Ident) = pat match {
+ case Bind(name, _) => (pat, Ident(name))
+ case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id)
+ case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id)
+ case _ =>
+ val name = ctx.freshName().toTermName
+ (Bind(name, pat), Ident(name))
+ }
+
+ /** Add MaybeFilter attachment */
+ def orFilter(tree: Tree): tree.type = {
+ tree.putAttachment(MaybeFilter, ())
+ tree
+ }
+
+ /** Make a pattern filter:
+ * rhs.withFilter { case pat => true case _ => false }
+ *
+ * On handling irrefutable patterns:
+ * The idea is to wait until the pattern matcher sees a call
+ *
+ * xs withFilter { cases }
+ *
+ * where cases can be proven to be refutable i.e. cases would be
+ * equivalent to { case _ => true }
+ *
+ * In that case, compile to
+ *
+ * xs withFilter alwaysTrue
+ *
+ * where `alwaysTrue` is a predefined function value:
+ *
+ * val alwaysTrue: Any => Boolean = true
+ *
+ * In the libraries operations can take advantage of alwaysTrue to shortcircuit the
+ * withFilter call.
+ *
+ * def withFilter(f: Elem => Boolean) =
+ * if (f eq alwaysTrue) this // or rather identity filter monadic applied to this
+ * else real withFilter
+ */
+ def makePatFilter(rhs: Tree, pat: Tree): Tree = {
+ val cases = List(
+ CaseDef(pat, EmptyTree, Literal(Constant(true))),
+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))))
+ Apply(orFilter(Select(rhs, nme.withFilter)), makeCaseLambda(cases))
+ }
+
+ /** Is pattern `pat` irrefutable when matched against `rhs`?
+ * We only can do a simple syntactic check here; a more refined check
+ * is done later in the pattern matcher (see discussion in @makePatFilter).
+ */
+ def isIrrefutable(pat: Tree, rhs: Tree): Boolean = {
+ def matchesTuple(pats: List[Tree], rhs: Tree): Boolean = rhs match {
+ case Tuple(trees) => (pats corresponds trees)(isIrrefutable)
+ case Parens(rhs1) => matchesTuple(pats, rhs1)
+ case Block(_, rhs1) => matchesTuple(pats, rhs1)
+ case If(_, thenp, elsep) => matchesTuple(pats, thenp) && matchesTuple(pats, elsep)
+ case Match(_, cases) => cases forall (matchesTuple(pats, _))
+ case CaseDef(_, _, rhs1) => matchesTuple(pats, rhs1)
+ case Throw(_) => true
+ case _ => false
+ }
+ pat match {
+ case Bind(_, pat1) => isIrrefutable(pat1, rhs)
+ case Parens(pat1) => isIrrefutable(pat1, rhs)
+ case Tuple(pats) => matchesTuple(pats, rhs)
+ case _ => isVarPattern(pat)
+ }
+ }
+
+ def isIrrefutableGenFrom(gen: GenFrom): Boolean =
+ gen.isInstanceOf[IrrefutableGenFrom] || isIrrefutable(gen.pat, gen.expr)
+
+ /** rhs.name with a pattern filter on rhs unless `pat` is irrefutable when
+ * matched against `rhs`.
+ */
+ def rhsSelect(gen: GenFrom, name: TermName) = {
+ val rhs = if (isIrrefutableGenFrom(gen)) gen.expr else makePatFilter(gen.expr, gen.pat)
+ Select(rhs, name)
+ }
+
+ enums match {
+ case (gen: GenFrom) :: Nil =>
+ Apply(rhsSelect(gen, mapName), makeLambda(gen.pat, body))
+ case (gen: GenFrom) :: (rest @ (GenFrom(_, _) :: _)) =>
+ val cont = makeFor(mapName, flatMapName, rest, body)
+ Apply(rhsSelect(gen, flatMapName), makeLambda(gen.pat, cont))
+ case (enum @ GenFrom(pat, rhs)) :: (rest @ GenAlias(_, _) :: _) =>
+ val (valeqs, rest1) = rest.span(_.isInstanceOf[GenAlias])
+ val pats = valeqs map { case GenAlias(pat, _) => pat }
+ val rhss = valeqs map { case GenAlias(_, rhs) => rhs }
+ val (defpat0, id0) = makeIdPat(pat)
+ val (defpats, ids) = (pats map makeIdPat).unzip
+ val pdefs = (valeqs, defpats, rhss).zipped.map(makePatDef(_, Modifiers(), _, _))
+ val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, rhs) :: Nil, Block(pdefs, makeTuple(id0 :: ids)))
+ val allpats = pat :: pats
+ val vfrom1 = new IrrefutableGenFrom(makeTuple(allpats), rhs1)
+ makeFor(mapName, flatMapName, vfrom1 :: rest1, body)
+ case (gen: GenFrom) :: test :: rest =>
+ val filtered = Apply(orFilter(rhsSelect(gen, nme.withFilter)), makeLambda(gen.pat, test))
+ val genFrom =
+ if (isIrrefutableGenFrom(gen)) new IrrefutableGenFrom(gen.pat, filtered)
+ else GenFrom(gen.pat, filtered)
+ makeFor(mapName, flatMapName, genFrom :: rest, body)
+ case _ =>
+ EmptyTree //may happen for erroneous input
+ }
+ }
+
+ // begin desugar
+ tree match {
+ case SymbolLit(str) =>
+ Apply(
+ ref(defn.SymbolClass.companionModule.termRef),
+ Literal(Constant(str)) :: Nil)
+ case InterpolatedString(id, segments) =>
+ val strs = segments map {
+ case ts: Thicket => ts.trees.head
+ case t => t
+ }
+ val elems = segments flatMap {
+ case ts: Thicket => ts.trees.tail
+ case t => Nil
+ }
+ Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems)
+ case InfixOp(l, op, r) =>
+ if (ctx.mode is Mode.Type)
+ if (op == tpnme.raw.AMP) AndTypeTree(l, r) // l & r
+ else if (op == tpnme.raw.BAR) OrTypeTree(l, r) // l | r
+ else AppliedTypeTree(Ident(op), l :: r :: Nil) // op[l, r]
+ else if (ctx.mode is Mode.Pattern)
+ Apply(Ident(op), l :: r :: Nil) // op(l, r)
+ else // l.op(r), or val x = r; l.op(x), plus handle named args specially
+ makeBinop(l, op, r)
+ case PostfixOp(t, op) =>
+ if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) {
+ val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType
+ Annotated(
+ AppliedTypeTree(ref(seqType), t),
+ New(ref(defn.RepeatedAnnotType), Nil :: Nil))
+ } else {
+ assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode)
+ Select(t, op)
+ }
+ case PrefixOp(op, t) =>
+ Select(t, nme.UNARY_PREFIX ++ op)
+ case Parens(t) =>
+ t
+ case Tuple(ts) =>
+ val arity = ts.length
+ def tupleTypeRef = defn.TupleType(arity)
+ if (arity > Definitions.MaxTupleArity) {
+ ctx.error(TupleTooLong(ts), tree.pos)
+ unitLiteral
+ } else if (arity == 1) ts.head
+ else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts)
+ else if (arity == 0) unitLiteral
+ else Apply(ref(tupleTypeRef.classSymbol.companionModule.valRef), ts)
+ case WhileDo(cond, body) =>
+ // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() }
+ val call = Apply(Ident(nme.WHILE_PREFIX), Nil)
+ val rhs = If(cond, Block(body, call), unitLiteral)
+ labelDefAndCall(nme.WHILE_PREFIX, rhs, call)
+ case DoWhile(body, cond) =>
+ // { label def doWhile$(): Unit = { body; if (cond) doWhile$() } ; doWhile$() }
+ val call = Apply(Ident(nme.DO_WHILE_PREFIX), Nil)
+ val rhs = Block(body, If(cond, call, unitLiteral))
+ labelDefAndCall(nme.DO_WHILE_PREFIX, rhs, call)
+ case ForDo(enums, body) =>
+ makeFor(nme.foreach, nme.foreach, enums, body) orElse tree
+ case ForYield(enums, body) =>
+ makeFor(nme.map, nme.flatMap, enums, body) orElse tree
+ case PatDef(mods, pats, tpt, rhs) =>
+ val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
+ flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
+ case ParsedTry(body, handler, finalizer) =>
+ handler match {
+ case Match(EmptyTree, cases) => Try(body, cases, finalizer)
+ case EmptyTree => Try(body, Nil, finalizer)
+ case _ =>
+ Try(body,
+ List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))),
+ finalizer)
+ }
+
+ }
+ }.withPos(tree.pos)
+
+ /** Create a class definition with the same info as the refined type given by `parent`
+ * and `refinements`.
+ *
+ * parent { refinements }
+ * ==>
+ * trait <refinement> extends core { this: self => refinements }
+ *
+ * Here, `core` is the (possibly parameterized) class part of `parent`.
+ * If `parent` is the same as `core`, self is empty. Otherwise `self` is `parent`.
+ *
+ * Example: Given
+ *
+ * class C
+ * type T1 = C { type T <: A }
+ *
+ * the refined type
+ *
+ * T1 { type T <: B }
+ *
+ * is expanded to
+ *
+ * trait <refinement> extends C { this: T1 => type T <: A }
+ *
+ * The result of this method is used for validity checking, is thrown away afterwards.
+ * @param parent The type of `parent`
+ */
+ def refinedTypeToClass(parent: tpd.Tree, refinements: List[Tree])(implicit ctx: Context): TypeDef = {
+ def stripToCore(tp: Type): List[Type] = tp match {
+ case tp: RefinedType if tp.argInfos.nonEmpty => tp :: Nil // parameterized class type
+ case tp: TypeRef if tp.symbol.isClass => tp :: Nil // monomorphic class type
+ case tp: TypeProxy => stripToCore(tp.underlying)
+ case AndType(tp1, tp2) => stripToCore(tp1) ::: stripToCore(tp2)
+ case _ => defn.AnyType :: Nil
+ }
+ val parentCores = stripToCore(parent.tpe)
+ val untpdParent = TypedSplice(parent)
+ val (classParents, self) =
+ if (parentCores.length == 1 && (parent.tpe eq parentCores.head)) (untpdParent :: Nil, EmptyValDef)
+ else (parentCores map TypeTree, ValDef(nme.WILDCARD, untpdParent, EmptyTree))
+ val impl = Template(emptyConstructor, classParents, self, refinements)
+ TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait)
+ }
+
+ /** If tree is a variable pattern, return its name and type, otherwise return None.
+ */
+ private object VarPattern {
+ def unapply(tree: Tree)(implicit ctx: Context): Option[VarInfo] = tree match {
+ case id: Ident => Some(id, TypeTree())
+ case Typed(id: Ident, tpt) => Some((id, tpt))
+ case _ => None
+ }
+ }
+
+ /** Returns list of all pattern variables, possibly with their types,
+ * without duplicates
+ */
+ private def getVariables(tree: Tree)(implicit ctx: Context): List[VarInfo] = {
+ val buf = new ListBuffer[VarInfo]
+ def seenName(name: Name) = buf exists (_._1.name == name)
+ def add(named: NameTree, t: Tree): Unit =
+ if (!seenName(named.name)) buf += ((named, t))
+ def collect(tree: Tree): Unit = tree match {
+ case Bind(nme.WILDCARD, tree1) =>
+ collect(tree1)
+ case tree @ Bind(_, Typed(tree1, tpt)) if !mayBeTypePat(tpt) =>
+ add(tree, tpt)
+ collect(tree1)
+ case tree @ Bind(_, tree1) =>
+ add(tree, TypeTree())
+ collect(tree1)
+ case Typed(id: Ident, t) if isVarPattern(id) && id.name != nme.WILDCARD && !isWildcardStarArg(tree) =>
+ add(id, t)
+ case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD =>
+ add(id, TypeTree())
+ case Apply(_, args) =>
+ args foreach collect
+ case Typed(expr, _) =>
+ collect(expr)
+ case NamedArg(_, arg) =>
+ collect(arg)
+ case SeqLiteral(elems, _) =>
+ elems foreach collect
+ case Alternative(trees) =>
+ for (tree <- trees; (vble, _) <- getVariables(tree))
+ ctx.error(IllegalVariableInPatternAlternative(), vble.pos)
+ case Annotated(arg, _) =>
+ collect(arg)
+ case InterpolatedString(_, segments) =>
+ segments foreach collect
+ case InfixOp(left, _, right) =>
+ collect(left)
+ collect(right)
+ case PrefixOp(_, od) =>
+ collect(od)
+ case Parens(tree) =>
+ collect(tree)
+ case Tuple(trees) =>
+ trees foreach collect
+ case _ =>
+ }
+ collect(tree)
+ buf.toList
+ }
+
+ private class IrrefutableGenFrom(pat: Tree, expr: Tree) extends GenFrom(pat, expr)
+}
diff --git a/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala
new file mode 100644
index 000000000..33aa87d8e
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala
@@ -0,0 +1,82 @@
+package dotty.tools.dotc
+package ast
+
+import core.Contexts.Context
+import core.Decorators._
+import util.Positions._
+import Trees.{MemberDef, DefTree}
+
+/** Utility functions to go from typed to untyped ASTs */
+object NavigateAST {
+
+ /** The untyped tree corresponding to typed tree `tree` in the compilation
+ * unit specified by `ctx`
+ */
+ def toUntyped(tree: tpd.Tree)(implicit ctx: Context): untpd.Tree =
+ untypedPath(tree, exactMatch = true) match {
+ case (utree: untpd.Tree) :: _ =>
+ utree
+ case _ =>
+ val loosePath = untypedPath(tree, exactMatch = false)
+ throw new
+ Error(i"""no untyped tree for $tree, pos = ${tree.pos}
+ |best matching path =\n$loosePath%\n====\n%
+ |path positions = ${loosePath.map(_.pos)}""")
+ }
+
+ /** The reverse path of untyped trees starting with a tree that closest matches
+ * `tree` and ending in the untyped tree at the root of the compilation unit
+ * specified by `ctx`.
+ * @param exactMatch If `true`, the path must start with a node that exactly
+ * matches `tree`, or `Nil` is returned.
+ * If `false` the path might start with a node enclosing
+ * the logical position of `tree`.
+ * Note: A complication concerns member definitions. ValDefs and DefDefs
+ * have after desugaring a position that spans just the name of the symbol being
+ * defined and nothing else. So we look instead for an untyped tree approximating the
+ * envelope of the definition, and declare success if we find another DefTree.
+ */
+ def untypedPath(tree: tpd.Tree, exactMatch: Boolean = false)(implicit ctx: Context): List[Positioned] =
+ tree match {
+ case tree: MemberDef[_] =>
+ untypedPath(tree.pos) match {
+ case path @ (last: DefTree[_]) :: _ => path
+ case path if !exactMatch => path
+ case _ => Nil
+ }
+ case _ =>
+ untypedPath(tree.pos) match {
+ case (path @ last :: _) if last.pos == tree.pos || !exactMatch => path
+ case _ => Nil
+ }
+ }
+
+ /** The reverse part of the untyped root of the compilation unit of `ctx` to
+ * position `pos`.
+ */
+ def untypedPath(pos: Position)(implicit ctx: Context): List[Positioned] =
+ pathTo(pos, ctx.compilationUnit.untpdTree)
+
+
+ /** The reverse path from node `from` to the node that closest encloses position `pos`,
+ * or `Nil` if no such path exists. If a non-empty path is returned it starts with
+ * the node closest enclosing `pos` and ends with `from`.
+ */
+ def pathTo(pos: Position, from: Positioned)(implicit ctx: Context): List[Positioned] = {
+ def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = {
+ while (it.hasNext) {
+ val path1 = it.next match {
+ case p: Positioned => singlePath(p, path)
+ case xs: List[_] => childPath(xs.iterator, path)
+ case _ => path
+ }
+ if (path1 ne path) return path1
+ }
+ path
+ }
+ def singlePath(p: Positioned, path: List[Positioned]): List[Positioned] =
+ if (p.pos contains pos) childPath(p.productIterator, p :: path)
+ else path
+ singlePath(from, Nil)
+ }
+} \ No newline at end of file
diff --git a/compiler/src/dotty/tools/dotc/ast/PluggableTransformers.scala b/compiler/src/dotty/tools/dotc/ast/PluggableTransformers.scala
new file mode 100644
index 000000000..a584230a2
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/PluggableTransformers.scala
@@ -0,0 +1,105 @@
+package dotty.tools.dotc
+package ast
+
+
+object PluggableTransformers {
+/*
+ import Trees._, Contexts._
+
+ abstract class PluggableTransformer[T] extends TreeTransformer[T, Context] {
+ type PluginOp[-N <: Tree[T]] = N => Tree[T]
+
+ private[this] var _ctx: Context = _
+ private[this] var _oldTree: Tree[T] = _
+
+ protected implicit def ctx: Context = _ctx
+ protected def oldTree: Tree[T] = _oldTree
+ protected def thisTransformer: PluggableTransformer[T] = this
+
+ class PluginOps[-N <: Tree[T]](op: PluginOp[N], val next: Plugins) {
+ def apply(tree: N, old: Tree[T], c: Context): Tree[T] = {
+ val savedCtx = _ctx
+ val savedOld = _oldTree
+ try {
+ op(tree)
+ } finally {
+ _oldTree = savedOld
+ _ctx = savedCtx
+ }
+ }
+ }
+
+ val NoOp: PluginOp[Tree[T]] = identity
+ val NoOps = new PluginOps(NoOp, null)
+
+ class Plugins {
+ def next: Plugins = null
+
+ def processIdent: PluginOp[Ident[T]] = NoOp
+ def processSelect: PluginOp[Select[T]] = NoOp
+
+ val IdentOps: PluginOps[Ident[T]] = NoOps
+ val SelectOps: PluginOps[Select[T]] = NoOps
+ }
+
+ val EmptyPlugin = new Plugins
+
+ private[this] var _plugins: Plugins = EmptyPlugin
+
+ override def plugins: Plugins = _plugins
+
+ class Plugin extends Plugins {
+ override val next = _plugins
+ _plugins = this
+
+ private def push[N <: Tree[T]](op: PluginOp[N], ops: => PluginOps[N]): PluginOps[N] =
+ if (op == NoOp) ops else new PluginOps(op, next)
+
+ override val IdentOps: PluginOps[Ident[T]] = push(processIdent, next.IdentOps)
+ override val SelectOps: PluginOps[Select[T]] = push(processSelect, next.SelectOps)
+ }
+
+ def postIdent(tree: Ident[T], old: Tree[T], c: Context, ops: PluginOps[Ident[T]]) =
+ if (ops eq NoOps) tree
+ else finishIdent(ops(tree, old, c), old, c, ops.next)
+
+ override def finishIdent(tree: Tree[T], old: Tree[T], c: Context, plugins: Plugins): Tree[T] = tree match {
+ case tree: Ident[_] => postIdent(tree, old, c, plugins.IdentOps)
+ case _ => postProcess(tree, old, c, plugins)
+ }
+
+ def postSelect(tree: Select[T], old: Tree[T], c: Context, ops: PluginOps[Select[T]]) =
+ if (ops eq NoOps) tree
+ else finishSelect(ops(tree, old, c), old, c, ops.next)
+
+ override def finishSelect(tree: Tree[T], old: Tree[T], c: Context, plugins: Plugins): Tree[T] = tree match {
+ case tree: Select[_] => postSelect(tree, old, c, plugins.SelectOps)
+ case _ => postProcess(tree, old, c, plugins)
+ }
+
+ protected def postProcess(tree: Tree[T], old: Tree[T], c: Context, plugins: Plugins): Tree[T] = tree match {
+ case tree: Ident[_] => finishIdent(tree, old, c, plugins)
+ case tree: Select[_] => finishSelect(tree, old, c, plugins)
+ }
+ }
+}
+
+import PluggableTransformers._, Types._, Trees._, Contexts._
+
+class ExampleTransformer extends PluggableTransformer[Type] {
+
+ object ExamplePlugin extends Plugin {
+ override def processIdent = {
+ case tree @ Ident(x) if x.isTypeName => tree.derivedSelect(tree, x)
+ case tree => tpd.Ident(???)
+ }
+ override def processSelect = { tree =>
+ if (tree.isType) tree.derivedIdent(tree.name)
+ else tpd.EmptyTree
+ }
+ }
+
+ override def transform(tree: tpd.Tree, ctx: Context) =
+ super.transform(tree, ctx)
+*/
+}
diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala
new file mode 100644
index 000000000..bb6817603
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala
@@ -0,0 +1,213 @@
+package dotty.tools.dotc
+package ast
+
+import util.Positions._
+import util.DotClass
+import core.Contexts.Context
+import core.Decorators._
+import core.Flags.JavaDefined
+import core.StdNames.nme
+
+/** A base class for things that have positions (currently: modifiers and trees)
+ */
+abstract class Positioned extends DotClass with Product {
+
+ private[this] var curPos: Position = _
+
+ setPos(initialPos)
+
+ /** The item's position.
+ */
+ def pos: Position = curPos
+
+ /** Destructively update `curPos` to given position. Also, set any missing
+ * positions in children.
+ */
+ protected def setPos(pos: Position): Unit = {
+ setPosUnchecked(pos)
+ if (pos.exists) setChildPositions(pos.toSynthetic)
+ }
+
+ /** A positioned item like this one with the position set to `pos`.
+ * if the positioned item is source-derived, a clone is returned.
+ * If the positioned item is synthetic, the position is updated
+ * destructively and the item itself is returned.
+ */
+ def withPos(pos: Position): this.type = {
+ val newpd = (if (pos == curPos || curPos.isSynthetic) this else clone).asInstanceOf[Positioned]
+ newpd.setPos(pos)
+ newpd.asInstanceOf[this.type]
+ }
+
+ def withPos(posd: Positioned): this.type =
+ if (posd == null) this else withPos(posd.pos)
+
+ /** This item with a position that's the union of the given `pos` and the
+ * current position.
+ */
+ def addPos(pos: Position): this.type = withPos(pos union this.pos)
+
+ /** Set position of this tree only, without performing
+ * any checks of consistency with - or updates of - other positions.
+ * Called from Unpickler when entering positions.
+ */
+ private[dotc] def setPosUnchecked(pos: Position) = curPos = pos
+
+ /** If any children of this node do not have positions,
+ * fit their positions between the positions of the known subtrees
+ * and transitively visit their children.
+ * The method is likely time-critical because it is invoked on any node
+ * we create, so we want to avoid object allocations in the common case.
+ * The method is naturally expressed as two mutually (tail-)recursive
+ * functions, one which computes the next element to consider or terminates if there
+ * is none and the other which propagates the position information to that element.
+ * But since mutual tail recursion is not supported in Scala, we express it instead
+ * as a while loop with a termination by return in the middle.
+ */
+ private def setChildPositions(pos: Position): Unit = {
+ var n = productArity // subnodes are analyzed right to left
+ var elems: List[Any] = Nil // children in lists still to be considered, from right to left
+ var end = pos.end // the last defined offset, fill in positions up to this offset
+ var outstanding: List[Positioned] = Nil // nodes that need their positions filled once a start position
+ // is known, from left to right.
+ def fillIn(ps: List[Positioned], start: Int, end: Int): Unit = ps match {
+ case p :: ps1 =>
+ p.setPos(Position(start, end))
+ fillIn(ps1, end, end)
+ case nil =>
+ }
+ while (true) {
+ var nextChild: Any = null // the next child to be considered
+ if (elems.nonEmpty) {
+ nextChild = elems.head
+ elems = elems.tail
+ }
+ else if (n > 0) {
+ n = n - 1
+ nextChild = productElement(n)
+ }
+ else {
+ fillIn(outstanding, pos.start, end)
+ return
+ }
+ nextChild match {
+ case p: Positioned =>
+ if (p.pos.exists) {
+ fillIn(outstanding, p.pos.end, end)
+ outstanding = Nil
+ end = p.pos.start
+ }
+ else outstanding = p :: outstanding
+ case xs: List[_] =>
+ elems = elems ::: xs.reverse
+ case _ =>
+ }
+ }
+ }
+
+ /** The initial, synthetic position. This is usually the union of all positioned children's positions.
+ */
+ def initialPos: Position = {
+ var n = productArity
+ var pos = NoPosition
+ while (n > 0) {
+ n -= 1
+ productElement(n) match {
+ case p: Positioned => pos = pos union p.pos
+ case xs: List[_] => pos = unionPos(pos, xs)
+ case _ =>
+ }
+ }
+ pos.toSynthetic
+ }
+
+ private def unionPos(pos: Position, xs: List[_]): Position = xs match {
+ case (p: Positioned) :: xs1 => unionPos(pos union p.pos, xs1)
+ case _ => pos
+ }
+
+ def contains(that: Positioned): Boolean = {
+ def isParent(x: Any): Boolean = x match {
+ case x: Positioned =>
+ x contains that
+ case xs: List[_] =>
+ xs exists isParent
+ case _ =>
+ false
+ }
+ (this eq that) ||
+ (this.pos contains that.pos) && {
+ var n = productArity
+ var found = false
+ while (!found && n > 0) {
+ n -= 1
+ found = isParent(productElement(n))
+ }
+ found
+ }
+ }
+
+ /** Check that all positioned items in this tree satisfy the following conditions:
+ * - Parent positions contain child positions
+ * - If item is a non-empty tree, it has a position
+ */
+ def checkPos(nonOverlapping: Boolean)(implicit ctx: Context): Unit = try {
+ import untpd._
+ var lastPositioned: Positioned = null
+ var lastPos = NoPosition
+ def check(p: Any): Unit = p match {
+ case p: Positioned =>
+ assert(pos contains p.pos,
+ s"""position error, parent position does not contain child positon
+ |parent = $this,
+ |parent position = $pos,
+ |child = $p,
+ |child position = ${p.pos}""".stripMargin)
+ p match {
+ case tree: Tree if !tree.isEmpty =>
+ assert(tree.pos.exists,
+ s"position error: position not set for $tree # ${tree.uniqueId}")
+ case _ =>
+ }
+ if (nonOverlapping) {
+ this match {
+ case _: WildcardFunction
+ if lastPositioned.isInstanceOf[ValDef] && !p.isInstanceOf[ValDef] =>
+ // ignore transition from last wildcard parameter to body
+ case _ =>
+ assert(!lastPos.exists || !p.pos.exists || lastPos.end <= p.pos.start,
+ s"""position error, child positions overlap or in wrong order
+ |parent = $this
+ |1st child = $lastPositioned
+ |1st child position = $lastPos
+ |2nd child = $p
+ |2nd child position = ${p.pos}""".stripMargin)
+ }
+ lastPositioned = p
+ lastPos = p.pos
+ }
+ p.checkPos(nonOverlapping)
+ case xs: List[_] =>
+ xs.foreach(check)
+ case _ =>
+ }
+ this match {
+ case tree: DefDef if tree.name == nme.CONSTRUCTOR && tree.mods.is(JavaDefined) =>
+ // Special treatment for constructors coming from Java:
+ // Leave out tparams, they are copied with wrong positions from parent class
+ check(tree.mods)
+ check(tree.vparamss)
+ case _ =>
+ val end = productArity
+ var n = 0
+ while (n < end) {
+ check(productElement(n))
+ n += 1
+ }
+ }
+ } catch {
+ case ex: AssertionError =>
+ println(i"error while checking $this")
+ throw ex
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
new file mode 100644
index 000000000..d1e6bd38a
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -0,0 +1,733 @@
+package dotty.tools
+package dotc
+package ast
+
+import core._
+import Flags._, Trees._, Types._, Contexts._
+import Names._, StdNames._, NameOps._, Decorators._, Symbols._
+import util.HashSet
+import typer.ConstFold
+
+trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
+ import TreeInfo._
+
+ // Note: the <: Type constraint looks necessary (and is needed to make the file compile in dotc).
+ // But Scalac accepts the program happily without it. Need to find out why.
+
+ def unsplice[T >: Untyped](tree: Trees.Tree[T]): Trees.Tree[T] = tree.asInstanceOf[untpd.Tree] match {
+ case untpd.TypedSplice(tree1) => tree1.asInstanceOf[Trees.Tree[T]]
+ case _ => tree
+ }
+
+ def isDeclarationOrTypeDef(tree: Tree): Boolean = unsplice(tree) match {
+ case DefDef(_, _, _, _, EmptyTree)
+ | ValDef(_, _, EmptyTree)
+ | TypeDef(_, _) => true
+ case _ => false
+ }
+
+ /** The largest subset of {NoInits, PureInterface} that a
+ * trait enclosing this statement can have as flags.
+ * Does tree contain an initialization part when seen as a member of a class or trait?
+ */
+ def defKind(tree: Tree): FlagSet = unsplice(tree) match {
+ case EmptyTree | _: Import => NoInitsInterface
+ case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
+ case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits
+ case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
+ case _ => EmptyFlags
+ }
+
+ def isOpAssign(tree: Tree) = unsplice(tree) match {
+ case Apply(fn, _ :: _) =>
+ unsplice(fn) match {
+ case Select(_, name) if name.isOpAssignmentName => true
+ case _ => false
+ }
+ case _ => false
+ }
+
+ class MatchingArgs(params: List[Symbol], args: List[Tree])(implicit ctx: Context) {
+ def foreach(f: (Symbol, Tree) => Unit): Boolean = {
+ def recur(params: List[Symbol], args: List[Tree]): Boolean = params match {
+ case Nil => args.isEmpty
+ case param :: params1 =>
+ if (param.info.isRepeatedParam) {
+ for (arg <- args) f(param, arg)
+ true
+ } else args match {
+ case Nil => false
+ case arg :: args1 =>
+ f(param, args.head)
+ recur(params1, args1)
+ }
+ }
+ recur(params, args)
+ }
+ def zipped: List[(Symbol, Tree)] = map((_, _))
+ def map[R](f: (Symbol, Tree) => R): List[R] = {
+ val b = List.newBuilder[R]
+ foreach(b += f(_, _))
+ b.result
+ }
+ }
+
+ /** The method part of an application node, possibly enclosed in a block
+ * with only valdefs as statements. the reason for also considering blocks
+ * is that named arguments can transform a call into a block, e.g.
+ * <init>(b = foo, a = bar)
+ * is transformed to
+ * { val x$1 = foo
+ * val x$2 = bar
+ * <init>(x$2, x$1)
+ * }
+ */
+ def methPart(tree: Tree): Tree = stripApply(tree) match {
+ case TypeApply(fn, _) => methPart(fn)
+ case AppliedTypeTree(fn, _) => methPart(fn) // !!! should not be needed
+ case Block(stats, expr) => methPart(expr)
+ case mp => mp
+ }
+
+ /** If this is an application, its function part, stripping all
+ * Apply nodes (but leaving TypeApply nodes in). Otherwise the tree itself.
+ */
+ def stripApply(tree: Tree): Tree = unsplice(tree) match {
+ case Apply(fn, _) => stripApply(fn)
+ case _ => tree
+ }
+
+ /** The number of arguments in an application */
+ def numArgs(tree: Tree): Int = unsplice(tree) match {
+ case Apply(fn, args) => numArgs(fn) + args.length
+ case TypeApply(fn, _) => numArgs(fn)
+ case Block(_, expr) => numArgs(expr)
+ case _ => 0
+ }
+
+ /** The (last) list of arguments of an application */
+ def arguments(tree: Tree): List[Tree] = unsplice(tree) match {
+ case Apply(_, args) => args
+ case TypeApply(fn, _) => arguments(fn)
+ case Block(_, expr) => arguments(expr)
+ case _ => Nil
+ }
+
+ /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the
+ * same object?
+ */
+ def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match {
+ case Ident(nme.CONSTRUCTOR) | Select(This(_), nme.CONSTRUCTOR) => true
+ case _ => false
+ }
+
+ /** Is tree a super constructor call?
+ */
+ def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match {
+ case Select(Super(_, _), nme.CONSTRUCTOR) => true
+ case _ => false
+ }
+
+ def isSuperSelection(tree: untpd.Tree) = unsplice(tree) match {
+ case Select(Super(_, _), _) => true
+ case _ => false
+ }
+
+ def isSelfOrSuperConstrCall(tree: Tree): Boolean = methPart(tree) match {
+ case Ident(nme.CONSTRUCTOR)
+ | Select(This(_), nme.CONSTRUCTOR)
+ | Select(Super(_, _), nme.CONSTRUCTOR) => true
+ case _ => false
+ }
+
+ /** Is tree a variable pattern? */
+ def isVarPattern(pat: untpd.Tree): Boolean = unsplice(pat) match {
+ case x: BackquotedIdent => false
+ case x: Ident => x.name.isVariableName
+ case _ => false
+ }
+
+ /** The first constructor definition in `stats` */
+ def firstConstructor(stats: List[Tree]): Tree = stats match {
+ case (meth: DefDef) :: _ if meth.name.isConstructorName => meth
+ case stat :: stats => firstConstructor(stats)
+ case nil => EmptyTree
+ }
+
+ /** The arguments to the first constructor in `stats`. */
+ def firstConstructorArgs(stats: List[Tree]): List[Tree] = firstConstructor(stats) match {
+ case DefDef(_, _, args :: _, _, _) => args
+ case _ => Nil
+ }
+
+ /** Is tpt a vararg type of the form T* or => T*? */
+ def isRepeatedParamType(tpt: Tree)(implicit ctx: Context): Boolean = tpt match {
+ case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1)
+ case tpt: TypeTree => tpt.typeOpt.isRepeatedParam
+ case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true
+ case _ => false
+ }
+
+ /** Is name a left-associative operator? */
+ def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.last != ':')
+
+ /** can this type be a type pattern? */
+ def mayBeTypePat(tree: untpd.Tree): Boolean = unsplice(tree) match {
+ case AndTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2)
+ case OrTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2)
+ case RefinedTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind])
+ case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind])
+ case Select(tpt, _) => mayBeTypePat(tpt)
+ case Annotated(tpt, _) => mayBeTypePat(tpt)
+ case _ => false
+ }
+
+ /** Is this argument node of the form <expr> : _* ?
+ */
+ def isWildcardStarArg(tree: Tree)(implicit ctx: Context): Boolean = unbind(tree) match {
+ case Typed(Ident(nme.WILDCARD_STAR), _) => true
+ case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true
+ case Typed(_, tpt: TypeTree) => tpt.hasType && tpt.tpe.isRepeatedParam
+ case _ => false
+ }
+
+ /** If this tree has type parameters, those. Otherwise Nil.
+ def typeParameters(tree: Tree): List[TypeDef] = tree match {
+ case DefDef(_, _, tparams, _, _, _) => tparams
+ case ClassDef(_, _, tparams, _) => tparams
+ case TypeDef(_, _, tparams, _) => tparams
+ case _ => Nil
+ }*/
+
+ /** Does this argument list end with an argument of the form <expr> : _* ? */
+ def isWildcardStarArgList(trees: List[Tree])(implicit ctx: Context) =
+ trees.nonEmpty && isWildcardStarArg(trees.last)
+
+ /** Is the argument a wildcard argument of the form `_` or `x @ _`?
+ */
+ def isWildcardArg(tree: Tree): Boolean = unbind(tree) match {
+ case Ident(nme.WILDCARD) => true
+ case _ => false
+ }
+
+ /** Does this list contain a named argument tree? */
+ def hasNamedArg(args: List[Any]) = args exists isNamedArg
+ val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]]
+
+ /** Is this pattern node a catch-all (wildcard or variable) pattern? */
+ def isDefaultCase(cdef: CaseDef) = cdef match {
+ case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat)
+ case _ => false
+ }
+
+ /** Is this pattern node a synthetic catch-all case, added during PartialFuction synthesis before we know
+ * whether the user provided cases are exhaustive. */
+ def isSyntheticDefaultCase(cdef: CaseDef) = unsplice(cdef) match {
+ case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree, _) => true
+ case _ => false
+ }
+
+ /** Does this CaseDef catch Throwable? */
+ def catchesThrowable(cdef: CaseDef)(implicit ctx: Context) =
+ catchesAllOf(cdef, defn.ThrowableType)
+
+ /** Does this CaseDef catch everything of a certain Type? */
+ def catchesAllOf(cdef: CaseDef, threshold: Type)(implicit ctx: Context) =
+ isDefaultCase(cdef) ||
+ cdef.guard.isEmpty && {
+ unbind(cdef.pat) match {
+ case Typed(Ident(nme.WILDCARD), tpt) => threshold <:< tpt.typeOpt
+ case _ => false
+ }
+ }
+
+ /** Is this case guarded? */
+ def isGuardedCase(cdef: CaseDef) = cdef.guard ne EmptyTree
+
+ /** The underlying pattern ignoring any bindings */
+ def unbind(x: Tree): Tree = unsplice(x) match {
+ case Bind(_, y) => unbind(y)
+ case y => y
+ }
+
+ /** Checks whether predicate `p` is true for all result parts of this expression,
+ * where we zoom into Ifs, Matches, and Blocks.
+ */
+ def forallResults(tree: Tree, p: Tree => Boolean): Boolean = tree match {
+ case If(_, thenp, elsep) => forallResults(thenp, p) && forallResults(elsep, p)
+ case Match(_, cases) => cases forall (c => forallResults(c.body, p))
+ case Block(_, expr) => forallResults(expr, p)
+ case _ => p(tree)
+ }
+}
+
+trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] =>
+ import TreeInfo._
+ import untpd._
+
+ /** True iff definition is a val or def with no right-hand-side, or it
+ * is an abstract typoe declaration
+ */
+ def lacksDefinition(mdef: MemberDef)(implicit ctx: Context) = mdef match {
+ case mdef: ValOrDefDef =>
+ mdef.unforcedRhs == EmptyTree && !mdef.name.isConstructorName && !mdef.mods.is(ParamAccessor)
+ case mdef: TypeDef =>
+ def isBounds(rhs: Tree): Boolean = rhs match {
+ case _: TypeBoundsTree => true
+ case PolyTypeTree(_, body) => isBounds(body)
+ case _ => false
+ }
+ mdef.rhs.isEmpty || isBounds(mdef.rhs)
+ case _ => false
+ }
+
+ def isFunctionWithUnknownParamType(tree: Tree) = tree match {
+ case Function(args, _) =>
+ args.exists {
+ case ValDef(_, tpt, _) => tpt.isEmpty
+ case _ => false
+ }
+ case _ => false
+ }
+
+ // todo: fill with other methods from TreeInfo that only apply to untpd.Tree's
+}
+
+trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
+ import TreeInfo._
+ import tpd._
+
+ /** The purity level of this statement.
+ * @return pure if statement has no side effects
+ * idempotent if running the statement a second time has no side effects
+ * impure otherwise
+ */
+ private def statPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match {
+ case EmptyTree
+ | TypeDef(_, _)
+ | Import(_, _)
+ | DefDef(_, _, _, _, _) =>
+ Pure
+ case vdef @ ValDef(_, _, _) =>
+ if (vdef.symbol.flags is Mutable) Impure else exprPurity(vdef.rhs)
+ case _ =>
+ Impure
+ // TODO: It seem like this should be exprPurity(tree)
+ // But if we do that the repl/vars test break. Need to figure out why that's the case.
+ }
+
+ /** The purity level of this expression.
+ * @return pure if expression has no side effects
+ * idempotent if running the expression a second time has no side effects
+ * impure otherwise
+ *
+ * Note that purity and idempotency are different. References to modules and lazy
+ * vals are impure (side-effecting) both because side-effecting code may be executed and because the first reference
+ * takes a different code path than all to follow; but they are idempotent
+ * because running the expression a second time gives the cached result.
+ */
+ private def exprPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match {
+ case EmptyTree
+ | This(_)
+ | Super(_, _)
+ | Literal(_)
+ | Closure(_, _, _) =>
+ Pure
+ case Ident(_) =>
+ refPurity(tree)
+ case Select(qual, _) =>
+ refPurity(tree).min(exprPurity(qual))
+ case TypeApply(fn, _) =>
+ exprPurity(fn)
+/*
+ * Not sure we'll need that. Comment out until we find out
+ case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX =>
+ // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm`
+ free.symbol.hasStableFlag && isIdempotentExpr(free)
+*/
+ case Apply(fn, args) =>
+ def isKnownPureOp(sym: Symbol) =
+ sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
+ // Note: After uncurry, field accesses are represented as Apply(getter, Nil),
+ // so an Apply can also be pure.
+ if (args.isEmpty && fn.symbol.is(Stable)) exprPurity(fn)
+ else if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol))
+ // A constant expression with pure arguments is pure.
+ minOf(exprPurity(fn), args.map(exprPurity))
+ else Impure
+ case Typed(expr, _) =>
+ exprPurity(expr)
+ case Block(stats, expr) =>
+ minOf(exprPurity(expr), stats.map(statPurity))
+ case _ =>
+ Impure
+ }
+
+ private def minOf(l0: PurityLevel, ls: List[PurityLevel]) = (l0 /: ls)(_ min _)
+
+ def isPureExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) == Pure
+ def isIdempotentExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) >= Idempotent
+
+ /** The purity level of this reference.
+ * @return
+ * pure if reference is (nonlazy and stable) or to a parameterized function
+ * idempotent if reference is lazy and stable
+ * impure otherwise
+ * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable
+ * flags set.
+ */
+ private def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel =
+ if (!tree.tpe.widen.isParameterless) Pure
+ else if (!tree.symbol.isStable) Impure
+ else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start.
+ else Pure
+
+ def isPureRef(tree: Tree)(implicit ctx: Context) =
+ refPurity(tree) == Pure
+ def isIdempotentRef(tree: Tree)(implicit ctx: Context) =
+ refPurity(tree) >= Idempotent
+
+ /** If `tree` is a constant expression, its value as a Literal,
+ * or `tree` itself otherwise.
+ *
+ * Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose.
+ * Example
+ *
+ * object O { final val x = 42; println("43") }
+ * O.x
+ *
+ * Strictly speaking we can't replace `O.x` with `42`. But this would make
+ * most expressions non-constant. Maybe we can change the spec to accept this
+ * kind of eliding behavior. Or else enforce true purity in the compiler.
+ * The choice will be affected by what we will do with `inline` and with
+ * Singleton type bounds (see SIP 23). Presumably
+ *
+ * object O1 { val x: Singleton = 42; println("43") }
+ * object O2 { inline val x = 42; println("43") }
+ *
+ * should behave differently.
+ *
+ * O1.x should have the same effect as { println("43"); 42 }
+ *
+ * whereas
+ *
+ * O2.x = 42
+ *
+ * Revisit this issue once we have implemented `inline`. Then we can demand
+ * purity of the prefix unless the selection goes to an inline val.
+ *
+ * Note: This method should be applied to all term tree nodes that are not literals,
+ * that can be idempotent, and that can have constant types. So far, only nodes
+ * of the following classes qualify:
+ *
+ * Ident
+ * Select
+ * TypeApply
+ */
+ def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = {
+ val tree1 = ConstFold(tree)
+ tree1.tpe.widenTermRefExpr match {
+ case ConstantType(value) if isIdempotentExpr(tree1) => Literal(value)
+ case _ => tree1
+ }
+ }
+
+ /** Is symbol potentially a getter of a mutable variable?
+ */
+ def mayBeVarGetter(sym: Symbol)(implicit ctx: Context): Boolean = {
+ def maybeGetterType(tpe: Type): Boolean = tpe match {
+ case _: ExprType | _: ImplicitMethodType => true
+ case tpe: PolyType => maybeGetterType(tpe.resultType)
+ case _ => false
+ }
+ sym.owner.isClass && !sym.isStable && maybeGetterType(sym.info)
+ }
+
+ /** Is tree a reference to a mutable variable, or to a potential getter
+ * that has a setter in the same class?
+ */
+ def isVariableOrGetter(tree: Tree)(implicit ctx: Context) = {
+ def sym = tree.symbol
+ def isVar = sym is Mutable
+ def isGetter =
+ mayBeVarGetter(sym) && sym.owner.info.member(sym.name.asTermName.setterName).exists
+
+ unsplice(tree) match {
+ case Ident(_) => isVar
+ case Select(_, _) => isVar || isGetter
+ case Apply(_, _) =>
+ methPart(tree) match {
+ case Select(qual, nme.apply) => qual.tpe.member(nme.update).exists
+ case _ => false
+ }
+ case _ => false
+ }
+ }
+
+ /** Is tree a `this` node which belongs to `enclClass`? */
+ def isSelf(tree: Tree, enclClass: Symbol)(implicit ctx: Context): Boolean = unsplice(tree) match {
+ case This(_) => tree.symbol == enclClass
+ case _ => false
+ }
+
+ /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */
+ def stripCast(tree: Tree)(implicit ctx: Context): Tree = {
+ def isCast(sel: Tree) = sel.symbol == defn.Any_asInstanceOf
+ unsplice(tree) match {
+ case TypeApply(sel @ Select(inner, _), _) if isCast(sel) =>
+ stripCast(inner)
+ case Apply(TypeApply(sel @ Select(inner, _), _), Nil) if isCast(sel) =>
+ stripCast(inner)
+ case t =>
+ t
+ }
+ }
+
+ /** Decompose a call fn[targs](vargs_1)...(vargs_n)
+ * into its constituents (where targs, vargss may be empty)
+ */
+ def decomposeCall(tree: Tree): (Tree, List[Tree], List[List[Tree]]) = tree match {
+ case Apply(fn, args) =>
+ val (meth, targs, argss) = decomposeCall(fn)
+ (meth, targs, argss :+ args)
+ case TypeApply(fn, targs) =>
+ val (meth, Nil, Nil) = decomposeCall(fn)
+ (meth, targs, Nil)
+ case _ =>
+ (tree, Nil, Nil)
+ }
+
+ /** An extractor for closures, either contained in a block or standalone.
+ */
+ object closure {
+ def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match {
+ case Block(_, Closure(env, meth, tpt)) => Some(env, meth, tpt)
+ case Closure(env, meth, tpt) => Some(env, meth, tpt)
+ case _ => None
+ }
+ }
+
+ /** If tree is a closure, its body, otherwise tree itself */
+ def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs
+ case _ => tree
+ }
+
+ /** The variables defined by a pattern, in reverse order of their appearance. */
+ def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = {
+ val acc = new TreeAccumulator[List[Symbol]] {
+ def apply(syms: List[Symbol], tree: Tree)(implicit ctx: Context) = tree match {
+ case Bind(_, body) => apply(tree.symbol :: syms, body)
+ case _ => foldOver(syms, tree)
+ }
+ }
+ acc(Nil, tree)
+ }
+
+ /** Is this pattern node a catch-all or type-test pattern? */
+ def isCatchCase(cdef: CaseDef)(implicit ctx: Context) = cdef match {
+ case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) =>
+ isSimpleThrowable(tpt.tpe)
+ case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) =>
+ isSimpleThrowable(tpt.tpe)
+ case _ =>
+ isDefaultCase(cdef)
+ }
+
+ private def isSimpleThrowable(tp: Type)(implicit ctx: Context): Boolean = tp match {
+ case tp @ TypeRef(pre, _) =>
+ (pre == NoPrefix || pre.widen.typeSymbol.isStatic) &&
+ (tp.symbol derivesFrom defn.ThrowableClass) && !(tp.symbol is Trait)
+ case _ =>
+ false
+ }
+
+ /** The symbols defined locally in a statement list */
+ def localSyms(stats: List[Tree])(implicit ctx: Context): List[Symbol] =
+ for (stat <- stats if stat.isDef && stat.symbol.exists) yield stat.symbol
+
+ /** If `tree` is a DefTree, the symbol defined by it, otherwise NoSymbol */
+ def definedSym(tree: Tree)(implicit ctx: Context): Symbol =
+ if (tree.isDef) tree.symbol else NoSymbol
+
+ /** Going from child to parent, the path of tree nodes that starts
+ * with a definition of symbol `sym` and ends with `root`, or Nil
+ * if no such path exists.
+ * Pre: `sym` must have a position.
+ */
+ def defPath(sym: Symbol, root: Tree)(implicit ctx: Context): List[Tree] = ctx.debugTraceIndented(s"defpath($sym with position ${sym.pos}, ${root.show})") {
+ require(sym.pos.exists)
+ object accum extends TreeAccumulator[List[Tree]] {
+ def apply(x: List[Tree], tree: Tree)(implicit ctx: Context): List[Tree] = {
+ if (tree.pos.contains(sym.pos))
+ if (definedSym(tree) == sym) tree :: x
+ else {
+ val x1 = foldOver(x, tree)
+ if (x1 ne x) tree :: x1 else x1
+ }
+ else x
+ }
+ }
+ accum(Nil, root)
+ }
+
+
+ /** The top level classes in this tree, including only those module classes that
+ * are not a linked class of some other class in the result.
+ */
+ def topLevelClasses(tree: Tree)(implicit ctx: Context): List[ClassSymbol] = tree match {
+ case PackageDef(_, stats) => stats.flatMap(topLevelClasses)
+ case tdef: TypeDef if tdef.symbol.isClass => tdef.symbol.asClass :: Nil
+ case _ => Nil
+ }
+
+ /** The tree containing only the top-level classes and objects matching either `cls` or its companion object */
+ def sliceTopLevel(tree: Tree, cls: ClassSymbol)(implicit ctx: Context): List[Tree] = tree match {
+ case PackageDef(pid, stats) =>
+ cpy.PackageDef(tree)(pid, stats.flatMap(sliceTopLevel(_, cls))) :: Nil
+ case tdef: TypeDef =>
+ val sym = tdef.symbol
+ assert(sym.isClass)
+ if (cls == sym || cls == sym.linkedClass) tdef :: Nil
+ else Nil
+ case vdef: ValDef =>
+ val sym = vdef.symbol
+ assert(sym is Module)
+ if (cls == sym.companionClass || cls == sym.moduleClass) vdef :: Nil
+ else Nil
+ case tree =>
+ tree :: Nil
+ }
+
+ /** The statement sequence that contains a definition of `sym`, or Nil
+ * if none was found.
+ * For a tree to be found, The symbol must have a position and its definition
+ * tree must be reachable from come tree stored in an enclosing context.
+ */
+ def definingStats(sym: Symbol)(implicit ctx: Context): List[Tree] =
+ if (!sym.pos.exists || (ctx eq NoContext) || ctx.compilationUnit == null) Nil
+ else defPath(sym, ctx.compilationUnit.tpdTree) match {
+ case defn :: encl :: _ =>
+ def verify(stats: List[Tree]) =
+ if (stats exists (definedSym(_) == sym)) stats else Nil
+ encl match {
+ case Block(stats, _) => verify(stats)
+ case encl: Template => verify(encl.body)
+ case PackageDef(_, stats) => verify(stats)
+ case _ => Nil
+ }
+ case nil =>
+ Nil
+ }
+}
+
+object TreeInfo {
+ class PurityLevel(val x: Int) extends AnyVal {
+ def >= (that: PurityLevel) = x >= that.x
+ def min(that: PurityLevel) = new PurityLevel(x min that.x)
+ }
+
+ val Pure = new PurityLevel(2)
+ val Idempotent = new PurityLevel(1)
+ val Impure = new PurityLevel(0)
+}
+
+ /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe)
+ def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation defn.SwitchClass
+ */
+
+ /** Does list of trees start with a definition of
+ * a class of module with given name (ignoring imports)
+ def firstDefinesClassOrObject(trees: List[Tree], name: Name): Boolean = trees match {
+ case Import(_, _) :: xs => firstDefinesClassOrObject(xs, name)
+ case Annotated(_, tree1) :: Nil => firstDefinesClassOrObject(List(tree1), name)
+ case ModuleDef(_, `name`, _) :: Nil => true
+ case ClassDef(_, `name`, _, _) :: Nil => true
+ case _ => false
+ }
+
+
+ /** Is this file the body of a compilation unit which should not
+ * have Predef imported?
+ */
+ def noPredefImportForUnit(body: Tree) = {
+ // Top-level definition whose leading imports include Predef.
+ def isLeadingPredefImport(defn: Tree): Boolean = defn match {
+ case PackageDef(_, defs1) => defs1 exists isLeadingPredefImport
+ case Import(expr, _) => isReferenceToPredef(expr)
+ case _ => false
+ }
+ // Compilation unit is class or object 'name' in package 'scala'
+ def isUnitInScala(tree: Tree, name: Name) = tree match {
+ case PackageDef(Ident(nme.scala_), defs) => firstDefinesClassOrObject(defs, name)
+ case _ => false
+ }
+
+ isUnitInScala(body, nme.Predef) || isLeadingPredefImport(body)
+ }
+ */
+
+ /*
+ def isAbsTypeDef(tree: Tree) = tree match {
+ case TypeDef(_, _, _, TypeBoundsTree(_, _)) => true
+ case TypeDef(_, _, _, rhs) => rhs.tpe.isInstanceOf[TypeBounds]
+ case _ => false
+ }
+
+ def isAliasTypeDef(tree: Tree) = tree match {
+ case TypeDef(_, _, _, _) => !isAbsTypeDef(tree)
+ case _ => false
+ }
+
+ /** Some handy extractors for spotting trees through the
+ * the haze of irrelevant braces: i.e. Block(Nil, SomeTree)
+ * should not keep us from seeing SomeTree.
+ */
+ abstract class SeeThroughBlocks[T] {
+ protected def unapplyImpl(x: Tree): T
+ def unapply(x: Tree): T = x match {
+ case Block(Nil, expr) => unapply(expr)
+ case _ => unapplyImpl(x)
+ }
+ }
+ object IsTrue extends SeeThroughBlocks[Boolean] {
+ protected def unapplyImpl(x: Tree): Boolean = x match {
+ case Literal(Constant(true)) => true
+ case _ => false
+ }
+ }
+ object IsFalse extends SeeThroughBlocks[Boolean] {
+ protected def unapplyImpl(x: Tree): Boolean = x match {
+ case Literal(Constant(false)) => true
+ case _ => false
+ }
+ }
+ object IsIf extends SeeThroughBlocks[Option[(Tree, Tree, Tree)]] {
+ protected def unapplyImpl(x: Tree) = x match {
+ case If(cond, thenp, elsep) => Some((cond, thenp, elsep))
+ case _ => None
+ }
+ }
+
+ object MacroImplReference {
+ private def refPart(tree: Tree): Tree = tree match {
+ case TypeApply(fun, _) => refPart(fun)
+ case ref: RefTree => ref
+ case _ => EmptyTree()
+ }
+
+ def unapply(tree: Tree) = refPart(tree) match {
+ case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, dissectApplied(tree).targs))
+ case _ => None
+ }
+ }
+
+ def isNullaryInvocation(tree: Tree): Boolean =
+ tree.symbol != null && tree.symbol.isMethod && (tree match {
+ case TypeApply(fun, _) => isNullaryInvocation(fun)
+ case tree: RefTree => true
+ case _ => false
+ })*/
+
+
+
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
new file mode 100644
index 000000000..cf529dfda
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
@@ -0,0 +1,187 @@
+package dotty.tools
+package dotc
+package ast
+
+import core._
+import Types._, Contexts._, Constants._, Names._, Flags._
+import SymDenotations._, Symbols._, Annotations._, Trees._, Symbols._
+import Denotations._, Decorators._
+import dotty.tools.dotc.transform.SymUtils._
+
+/** A map that applies three functions and a substitution together to a tree and
+ * makes sure they are coordinated so that the result is well-typed. The functions are
+ * @param typeMap A function from Type to Type that gets applied to the
+ * type of every tree node and to all locally defined symbols,
+ * followed by the substitution [substFrom := substTo].
+ * @param treeMap A transformer that translates all encountered subtrees in
+ * prefix traversal orders
+ * @param oldOwners Previous owners. If a top-level local symbol in the mapped tree
+ * has one of these as an owner, the owner is replaced by the corresponding
+ * symbol in `newOwners`.
+ * @param newOwners New owners, replacing previous owners.
+ * @param substFrom The symbols that need to be substituted.
+ * @param substTo The substitution targets.
+ *
+ * The reason the substitution is broken out from the rest of the type map is
+ * that all symbols have to be substituted at the same time. If we do not do this,
+ * we risk data races on named types. Example: Say we have `outer#1.inner#2` and we
+ * have two substitutions S1 = [outer#1 := outer#3], S2 = [inner#2 := inner#4] where
+ * hashtags precede symbol ids. If we do S1 first, we get outer#2.inner#3. If we then
+ * do S2 we get outer#2.inner#4. But that means that the named type outer#2.inner
+ * gets two different denotations in the same period. Hence, if -Yno-double-bindings is
+ * set, we would get a data race assertion error.
+ */
+final class TreeTypeMap(
+ val typeMap: Type => Type = IdentityTypeMap,
+ val treeMap: tpd.Tree => tpd.Tree = identity _,
+ val oldOwners: List[Symbol] = Nil,
+ val newOwners: List[Symbol] = Nil,
+ val substFrom: List[Symbol] = Nil,
+ val substTo: List[Symbol] = Nil)(implicit ctx: Context) extends tpd.TreeMap {
+ import tpd._
+
+ /** If `sym` is one of `oldOwners`, replace by corresponding symbol in `newOwners` */
+ def mapOwner(sym: Symbol) = sym.subst(oldOwners, newOwners)
+
+ /** Replace occurrences of `This(oldOwner)` in some prefix of a type
+ * by the corresponding `This(newOwner)`.
+ */
+ private val mapOwnerThis = new TypeMap {
+ private def mapPrefix(from: List[Symbol], to: List[Symbol], tp: Type): Type = from match {
+ case Nil => tp
+ case (cls: ClassSymbol) :: from1 => mapPrefix(from1, to.tail, tp.substThis(cls, to.head.thisType))
+ case _ :: from1 => mapPrefix(from1, to.tail, tp)
+ }
+ def apply(tp: Type): Type = tp match {
+ case tp: NamedType => tp.derivedSelect(mapPrefix(oldOwners, newOwners, tp.prefix))
+ case _ => mapOver(tp)
+ }
+ }
+
+ def mapType(tp: Type) =
+ mapOwnerThis(typeMap(tp).substSym(substFrom, substTo))
+
+ private def updateDecls(prevStats: List[Tree], newStats: List[Tree]): Unit =
+ if (prevStats.isEmpty) assert(newStats.isEmpty)
+ else {
+ prevStats.head match {
+ case pdef: MemberDef =>
+ val prevSym = pdef.symbol
+ val newSym = newStats.head.symbol
+ val newCls = newSym.owner.asClass
+ if (prevSym != newSym) newCls.replace(prevSym, newSym)
+ case _ =>
+ }
+ updateDecls(prevStats.tail, newStats.tail)
+ }
+
+ override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = treeMap(tree) match {
+ case impl @ Template(constr, parents, self, _) =>
+ val tmap = withMappedSyms(localSyms(impl :: self :: Nil))
+ cpy.Template(impl)(
+ constr = tmap.transformSub(constr),
+ parents = parents mapconserve transform,
+ self = tmap.transformSub(self),
+ body = impl.body mapconserve
+ (tmap.transform(_)(ctx.withOwner(mapOwner(impl.symbol.owner))))
+ ).withType(tmap.mapType(impl.tpe))
+ case tree1 =>
+ tree1.withType(mapType(tree1.tpe)) match {
+ case id: Ident if tpd.needsSelect(id.tpe) =>
+ ref(id.tpe.asInstanceOf[TermRef]).withPos(id.pos)
+ case ddef @ DefDef(name, tparams, vparamss, tpt, _) =>
+ val (tmap1, tparams1) = transformDefs(ddef.tparams)
+ val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss)
+ val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
+ res.symbol.transformAnnotations {
+ case ann: BodyAnnotation => ann.derivedAnnotation(res.rhs)
+ case ann => ann
+ }
+ res
+ case blk @ Block(stats, expr) =>
+ val (tmap1, stats1) = transformDefs(stats)
+ val expr1 = tmap1.transform(expr)
+ cpy.Block(blk)(stats1, expr1)
+ case inlined @ Inlined(call, bindings, expanded) =>
+ val (tmap1, bindings1) = transformDefs(bindings)
+ val expanded1 = tmap1.transform(expanded)
+ cpy.Inlined(inlined)(call, bindings1, expanded1)
+ case cdef @ CaseDef(pat, guard, rhs) =>
+ val tmap = withMappedSyms(patVars(pat))
+ val pat1 = tmap.transform(pat)
+ val guard1 = tmap.transform(guard)
+ val rhs1 = tmap.transform(rhs)
+ cpy.CaseDef(cdef)(pat1, guard1, rhs1)
+ case tree1 =>
+ super.transform(tree1)
+ }
+ }
+
+ override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) =
+ transformDefs(trees)._2
+
+ private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
+ val tmap = withMappedSyms(tpd.localSyms(trees))
+ (tmap, tmap.transformSub(trees))
+ }
+
+ private def transformVParamss(vparamss: List[List[ValDef]]): (TreeTypeMap, List[List[ValDef]]) = vparamss match {
+ case vparams :: rest =>
+ val (tmap1, vparams1) = transformDefs(vparams)
+ val (tmap2, vparamss2) = tmap1.transformVParamss(rest)
+ (tmap2, vparams1 :: vparamss2)
+ case nil =>
+ (this, vparamss)
+ }
+
+ def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree]
+
+ def apply(annot: Annotation): Annotation = annot.derivedAnnotation(apply(annot.tree))
+
+ /** The current tree map composed with a substitution [from -> to] */
+ def withSubstitution(from: List[Symbol], to: List[Symbol]): TreeTypeMap =
+ if (from eq to) this
+ else {
+ // assert that substitution stays idempotent, assuming its parts are
+ // TODO: It might be better to cater for the asserted-away conditions, by
+ // setting up a proper substitution abstraction with a compose operator that
+ // guarantees idempotence. But this might be too inefficient in some cases.
+ // We'll cross that bridge when we need to.
+ assert(!from.exists(substTo contains _))
+ assert(!to.exists(substFrom contains _))
+ assert(!from.exists(newOwners contains _))
+ assert(!to.exists(oldOwners contains _))
+ new TreeTypeMap(
+ typeMap,
+ treeMap,
+ from ++ oldOwners,
+ to ++ newOwners,
+ from ++ substFrom,
+ to ++ substTo)
+ }
+
+ /** Apply `typeMap` and `ownerMap` to given symbols `syms`
+ * and return a treemap that contains the substitution
+ * between original and mapped symbols.
+ */
+ def withMappedSyms(syms: List[Symbol], mapAlways: Boolean = false): TreeTypeMap =
+ withMappedSyms(syms, ctx.mapSymbols(syms, this, mapAlways))
+
+ /** The tree map with the substitution between originals `syms`
+ * and mapped symbols `mapped`. Also goes into mapped classes
+ * and substitutes their declarations.
+ */
+ def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = {
+ val symsChanged = syms ne mapped
+ val substMap = withSubstitution(syms, mapped)
+ val fullMap = (substMap /: mapped.filter(_.isClass)) { (tmap, cls) =>
+ val origDcls = cls.info.decls.toList
+ val mappedDcls = ctx.mapSymbols(origDcls, tmap)
+ val tmap1 = tmap.withMappedSyms(origDcls, mappedDcls)
+ if (symsChanged) (origDcls, mappedDcls).zipped.foreach(cls.asClass.replace)
+ tmap1
+ }
+ if (symsChanged || (fullMap eq substMap)) fullMap
+ else withMappedSyms(syms, mapAlways = true)
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala
new file mode 100644
index 000000000..2801bcae2
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala
@@ -0,0 +1,1295 @@
+package dotty.tools
+package dotc
+package ast
+
+import core._
+import Types._, Names._, Flags._, util.Positions._, Contexts._, Constants._
+import SymDenotations._, Symbols._, Denotations._, StdNames._, Comments._
+import annotation.tailrec
+import language.higherKinds
+import collection.IndexedSeqOptimized
+import collection.immutable.IndexedSeq
+import collection.mutable.ListBuffer
+import parsing.Tokens.Token
+import printing.Printer
+import util.{Stats, Attachment, Property, DotClass}
+import annotation.unchecked.uncheckedVariance
+import language.implicitConversions
+
+object Trees {
+
+ // Note: it would be more logical to make Untyped = Nothing.
+ // However, this interacts in a bad way with Scala's current type inference.
+ // In fact, we cannot write something like Select(pre, name), where pre is
+ // of type Tree[Nothing]; type inference will treat the Nothing as an uninstantiated
+ // value and will not infer Nothing as the type parameter for Select.
+ // We should come back to this issue once type inference is changed.
+ type Untyped = Null
+
+ /** The total number of created tree nodes, maintained if Stats.enabled */
+ @sharable var ntrees = 0
+
+ /** Property key for trees with documentation strings attached */
+ val DocComment = new Property.Key[Comment]
+
+ @sharable private var nextId = 0 // for debugging
+
+ type LazyTree = AnyRef /* really: Tree | Lazy[Tree] */
+ type LazyTreeList = AnyRef /* really: List[Tree] | Lazy[List[Tree]] */
+
+ /** Trees take a parameter indicating what the type of their `tpe` field
+ * is. Two choices: `Type` or `Untyped`.
+ * Untyped trees have type `Tree[Untyped]`.
+ *
+ * Tree typing uses a copy-on-write implementation:
+ *
+ * - You can never observe a `tpe` which is `null` (throws an exception)
+ * - So when creating a typed tree with `withType` we can re-use
+ * the existing tree transparently, assigning its `tpe` field,
+ * provided it was `null` before.
+ * - It is impossible to embed untyped trees in typed ones.
+ * - Typed trees can be embedded in untyped ones provided they are rooted
+ * in a TypedSplice node.
+ * - Type checking an untyped tree should remove all embedded `TypedSplice`
+ * nodes.
+ */
+ abstract class Tree[-T >: Untyped] extends Positioned
+ with Product
+ with Attachment.Container
+ with printing.Showable
+ with Cloneable {
+
+ if (Stats.enabled) ntrees += 1
+
+ private def nxId = {
+ nextId += 1
+ //assert(nextId != 199, this)
+ nextId
+ }
+
+ /** A unique identifier for this tree. Used for debugging, and potentially
+ * tracking presentation compiler interactions
+ */
+ private var myUniqueId: Int = nxId
+
+ def uniqueId = myUniqueId
+
+ /** The type constructor at the root of the tree */
+ type ThisTree[T >: Untyped] <: Tree[T]
+
+ private[this] var myTpe: T = _
+
+ /** Destructively set the type of the tree. This should be called only when it is known that
+ * it is safe under sharing to do so. One use-case is in the withType method below
+ * which implements copy-on-write. Another use-case is in method interpolateAndAdapt in Typer,
+ * where we overwrite with a simplified version of the type itself.
+ */
+ private[dotc] def overwriteType(tpe: T) = {
+ if (this.isInstanceOf[Template[_]]) assert(tpe.isInstanceOf[WithFixedSym], s"$this <--- $tpe")
+ myTpe = tpe
+ }
+
+ /** The type of the tree. In case of an untyped tree,
+ * an UnAssignedTypeException is thrown. (Overridden by empty trees)
+ */
+ def tpe: T @uncheckedVariance = {
+ if (myTpe == null)
+ throw new UnAssignedTypeException(this)
+ myTpe
+ }
+
+ /** Copy `tpe` attribute from tree `from` into this tree, independently
+ * whether it is null or not.
+ final def copyAttr[U >: Untyped](from: Tree[U]): ThisTree[T] = {
+ val t1 = this.withPos(from.pos)
+ val t2 =
+ if (from.myTpe != null) t1.withType(from.myTpe.asInstanceOf[Type])
+ else t1
+ t2.asInstanceOf[ThisTree[T]]
+ }
+ */
+
+ /** Return a typed tree that's isomorphic to this tree, but has given
+ * type. (Overridden by empty trees)
+ */
+ def withType(tpe: Type)(implicit ctx: Context): ThisTree[Type] = {
+ if (tpe == ErrorType) assert(ctx.reporter.errorsReported)
+ withTypeUnchecked(tpe)
+ }
+
+ def withTypeUnchecked(tpe: Type): ThisTree[Type] = {
+ val tree =
+ (if (myTpe == null ||
+ (myTpe.asInstanceOf[AnyRef] eq tpe.asInstanceOf[AnyRef])) this
+ else clone).asInstanceOf[Tree[Type]]
+ tree overwriteType tpe
+ tree.asInstanceOf[ThisTree[Type]]
+ }
+
+ /** Does the tree have its type field set? Note: this operation is not
+ * referentially transparent, because it can observe the withType
+ * modifications. Should be used only in special circumstances (we
+ * need it for printing trees with optional type info).
+ */
+ final def hasType: Boolean = myTpe != null
+
+ final def typeOpt: Type = myTpe match {
+ case tp: Type => tp
+ case _ => NoType
+ }
+
+ /** The denotation referred tno by this tree.
+ * Defined for `DenotingTree`s and `ProxyTree`s, NoDenotation for other
+ * kinds of trees
+ */
+ def denot(implicit ctx: Context): Denotation = NoDenotation
+
+ /** Shorthand for `denot.symbol`. */
+ final def symbol(implicit ctx: Context): Symbol = denot.symbol
+
+ /** Does this tree represent a type? */
+ def isType: Boolean = false
+
+ /** Does this tree represent a term? */
+ def isTerm: Boolean = false
+
+ /** Is this a legal part of a pattern which is not at the same time a term? */
+ def isPattern: Boolean = false
+
+ /** Does this tree define a new symbol that is not defined elsewhere? */
+ def isDef: Boolean = false
+
+ /** Is this tree either the empty tree or the empty ValDef or an empty type ident? */
+ def isEmpty: Boolean = false
+
+ /** Convert tree to a list. Gives a singleton list, except
+ * for thickets which return their element trees.
+ */
+ def toList: List[Tree[T]] = this :: Nil
+
+ /** if this tree is the empty tree, the alternative, else this tree */
+ def orElse[U >: Untyped <: T](that: => Tree[U]): Tree[U] =
+ if (this eq genericEmptyTree) that else this
+
+ /** The number of nodes in this tree */
+ def treeSize: Int = {
+ var s = 1
+ def addSize(elem: Any): Unit = elem match {
+ case t: Tree[_] => s += t.treeSize
+ case ts: List[_] => ts foreach addSize
+ case _ =>
+ }
+ productIterator foreach addSize
+ s
+ }
+
+ /** If this is a thicket, perform `op` on each of its trees
+ * otherwise, perform `op` ion tree itself.
+ */
+ def foreachInThicket(op: Tree[T] => Unit): Unit = op(this)
+
+ override def toText(printer: Printer) = printer.toText(this)
+
+ override def hashCode(): Int = uniqueId // for debugging; was: System.identityHashCode(this)
+ override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
+
+ override def clone: Tree[T] = {
+ val tree = super.clone.asInstanceOf[Tree[T]]
+ tree.myUniqueId = nxId
+ tree
+ }
+ }
+
+ class UnAssignedTypeException[T >: Untyped](tree: Tree[T]) extends RuntimeException {
+ override def getMessage: String = s"type of $tree is not assigned"
+ }
+
+ // ------ Categories of trees -----------------------------------
+
+ /** Instances of this class are trees for which isType is definitely true.
+ * Note that some trees have isType = true without being TypTrees (e.g. Ident, AnnotatedTree)
+ */
+ trait TypTree[-T >: Untyped] extends Tree[T] {
+ type ThisTree[-T >: Untyped] <: TypTree[T]
+ override def isType = true
+ }
+
+ /** Instances of this class are trees for which isTerm is definitely true.
+ * Note that some trees have isTerm = true without being TermTrees (e.g. Ident, AnnotatedTree)
+ */
+ trait TermTree[-T >: Untyped] extends Tree[T] {
+ type ThisTree[-T >: Untyped] <: TermTree[T]
+ override def isTerm = true
+ }
+
+ /** Instances of this class are trees which are not terms but are legal
+ * parts of patterns.
+ */
+ trait PatternTree[-T >: Untyped] extends Tree[T] {
+ type ThisTree[-T >: Untyped] <: PatternTree[T]
+ override def isPattern = true
+ }
+
+ /** Tree's denotation can be derived from its type */
+ abstract class DenotingTree[-T >: Untyped] extends Tree[T] {
+ type ThisTree[-T >: Untyped] <: DenotingTree[T]
+ override def denot(implicit ctx: Context) = tpe match {
+ case tpe: NamedType => tpe.denot
+ case tpe: ThisType => tpe.cls.denot
+ case tpe: AnnotatedType => tpe.stripAnnots match {
+ case tpe: NamedType => tpe.denot
+ case tpe: ThisType => tpe.cls.denot
+ case _ => NoDenotation
+ }
+ case _ => NoDenotation
+ }
+ }
+
+ /** Tree's denot/isType/isTerm properties come from a subtree
+ * identified by `forwardTo`.
+ */
+ abstract class ProxyTree[-T >: Untyped] extends Tree[T] {
+ type ThisTree[-T >: Untyped] <: ProxyTree[T]
+ def forwardTo: Tree[T]
+ override def denot(implicit ctx: Context): Denotation = forwardTo.denot
+ override def isTerm = forwardTo.isTerm
+ override def isType = forwardTo.isType
+ }
+
+ /** Tree has a name */
+ abstract class NameTree[-T >: Untyped] extends DenotingTree[T] {
+ type ThisTree[-T >: Untyped] <: NameTree[T]
+ def name: Name
+ }
+
+ /** Tree refers by name to a denotation */
+ abstract class RefTree[-T >: Untyped] extends NameTree[T] {
+ type ThisTree[-T >: Untyped] <: RefTree[T]
+ def qualifier: Tree[T]
+ override def isType = name.isTypeName
+ override def isTerm = name.isTermName
+ }
+
+ /** Tree defines a new symbol */
+ trait DefTree[-T >: Untyped] extends DenotingTree[T] {
+ type ThisTree[-T >: Untyped] <: DefTree[T]
+ override def isDef = true
+ def namedType = tpe.asInstanceOf[NamedType]
+ }
+
+ /** Tree defines a new symbol and carries modifiers.
+ * The position of a MemberDef contains only the defined identifier or pattern.
+ * The envelope of a MemberDef contains the whole definition and has its point
+ * on the opening keyword (or the next token after that if keyword is missing).
+ */
+ abstract class MemberDef[-T >: Untyped] extends NameTree[T] with DefTree[T] {
+ type ThisTree[-T >: Untyped] <: MemberDef[T]
+
+ private[this] var myMods: untpd.Modifiers = null
+
+ private[dotc] def rawMods: untpd.Modifiers =
+ if (myMods == null) untpd.EmptyModifiers else myMods
+
+ def rawComment: Option[Comment] = getAttachment(DocComment)
+
+ def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = {
+ val tree = if (myMods == null || (myMods == mods)) this else clone.asInstanceOf[MemberDef[Untyped]]
+ tree.setMods(mods)
+ tree.asInstanceOf[ThisTree[Untyped]]
+ }
+
+ def withFlags(flags: FlagSet): ThisTree[Untyped] = withMods(untpd.Modifiers(flags))
+
+ def setComment(comment: Option[Comment]): ThisTree[Untyped] = {
+ comment.map(putAttachment(DocComment, _))
+ asInstanceOf[ThisTree[Untyped]]
+ }
+
+ protected def setMods(mods: untpd.Modifiers) = myMods = mods
+
+ /** The position of the name defined by this definition.
+ * This is a point position if the definition is synthetic, or a range position
+ * if the definition comes from source.
+ * It might also be that the definition does not have a position (for instance when synthesized by
+ * a calling chain from `viewExists`), in that case the return position is NoPosition.
+ */
+ def namePos =
+ if (pos.exists)
+ if (rawMods.is(Synthetic)) Position(pos.point, pos.point)
+ else Position(pos.point, pos.point + name.length, pos.point)
+ else pos
+ }
+
+ /** A ValDef or DefDef tree */
+ trait ValOrDefDef[-T >: Untyped] extends MemberDef[T] with WithLazyField[Tree[T]] {
+ def tpt: Tree[T]
+ def unforcedRhs: LazyTree = unforced
+ def rhs(implicit ctx: Context): Tree[T] = forceIfLazy
+ }
+
+ // ----------- Tree case classes ------------------------------------
+
+ /** name */
+ case class Ident[-T >: Untyped] private[ast] (name: Name)
+ extends RefTree[T] {
+ type ThisTree[-T >: Untyped] = Ident[T]
+ def qualifier: Tree[T] = genericEmptyTree
+ }
+
+ class BackquotedIdent[-T >: Untyped] private[ast] (name: Name)
+ extends Ident[T](name) {
+ override def toString = s"BackquotedIdent($name)"
+ }
+
+ /** qualifier.name, or qualifier#name, if qualifier is a type */
+ case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name)
+ extends RefTree[T] {
+ type ThisTree[-T >: Untyped] = Select[T]
+ }
+
+ class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature)
+ extends Select[T](qualifier, name) {
+ override def toString = s"SelectWithSig($qualifier, $name, $sig)"
+ }
+
+ /** qual.this */
+ case class This[-T >: Untyped] private[ast] (qual: untpd.Ident)
+ extends DenotingTree[T] with TermTree[T] {
+ type ThisTree[-T >: Untyped] = This[T]
+ // Denotation of a This tree is always the underlying class; needs correction for modules.
+ override def denot(implicit ctx: Context): Denotation = {
+ tpe match {
+ case tpe @ TermRef(pre, _) if tpe.symbol is Module =>
+ tpe.symbol.moduleClass.denot.asSeenFrom(pre)
+ case _ =>
+ super.denot
+ }
+ }
+ }
+
+ /** C.super[mix], where qual = C.this */
+ case class Super[-T >: Untyped] private[ast] (qual: Tree[T], mix: untpd.Ident)
+ extends ProxyTree[T] with TermTree[T] {
+ type ThisTree[-T >: Untyped] = Super[T]
+ def forwardTo = qual
+ }
+
+ abstract class GenericApply[-T >: Untyped] extends ProxyTree[T] with TermTree[T] {
+ type ThisTree[-T >: Untyped] <: GenericApply[T]
+ val fun: Tree[T]
+ val args: List[Tree[T]]
+ def forwardTo = fun
+ }
+
+ /** fun(args) */
+ case class Apply[-T >: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])
+ extends GenericApply[T] {
+ type ThisTree[-T >: Untyped] = Apply[T]
+ }
+
+ /** fun[args] */
+ case class TypeApply[-T >: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])
+ extends GenericApply[T] {
+ type ThisTree[-T >: Untyped] = TypeApply[T]
+ }
+
+ /** const */
+ case class Literal[-T >: Untyped] private[ast] (const: Constant)
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = Literal[T]
+ }
+
+ /** new tpt, but no constructor call */
+ case class New[-T >: Untyped] private[ast] (tpt: Tree[T])
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = New[T]
+ }
+
+ /** expr : tpt */
+ case class Typed[-T >: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])
+ extends ProxyTree[T] with TermTree[T] {
+ type ThisTree[-T >: Untyped] = Typed[T]
+ def forwardTo = expr
+ }
+
+ /** name = arg, in a parameter list */
+ case class NamedArg[-T >: Untyped] private[ast] (name: Name, arg: Tree[T])
+ extends Tree[T] {
+ type ThisTree[-T >: Untyped] = NamedArg[T]
+ }
+
+ /** name = arg, outside a parameter list */
+ case class Assign[-T >: Untyped] private[ast] (lhs: Tree[T], rhs: Tree[T])
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = Assign[T]
+ }
+
+ /** { stats; expr } */
+ case class Block[-T >: Untyped] private[ast] (stats: List[Tree[T]], expr: Tree[T])
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = Block[T]
+ }
+
+ /** if cond then thenp else elsep */
+ case class If[-T >: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T])
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = If[T]
+ }
+
+ /** A closure with an environment and a reference to a method.
+ * @param env The captured parameters of the closure
+ * @param meth A ref tree that refers to the method of the closure.
+ * The first (env.length) parameters of that method are filled
+ * with env values.
+ * @param tpt Either EmptyTree or a TypeTree. If tpt is EmptyTree the type
+ * of the closure is a function type, otherwise it is the type
+ * given in `tpt`, which must be a SAM type.
+ */
+ case class Closure[-T >: Untyped] private[ast] (env: List[Tree[T]], meth: Tree[T], tpt: Tree[T])
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = Closure[T]
+ }
+
+ /** selector match { cases } */
+ case class Match[-T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = Match[T]
+ }
+
+ /** case pat if guard => body; only appears as child of a Match */
+ case class CaseDef[-T >: Untyped] private[ast] (pat: Tree[T], guard: Tree[T], body: Tree[T])
+ extends Tree[T] {
+ type ThisTree[-T >: Untyped] = CaseDef[T]
+ }
+
+ /** return expr
+ * where `from` refers to the method from which the return takes place
+ * After program transformations this is not necessarily the enclosing method, because
+ * closures can intervene.
+ */
+ case class Return[-T >: Untyped] private[ast] (expr: Tree[T], from: Tree[T] = genericEmptyTree)
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = Return[T]
+ }
+
+ /** try block catch handler finally finalizer
+ *
+ * Note: if the handler is a case block CASES of the form
+ *
+ * { case1 ... caseN }
+ *
+ * the parser returns Match(EmptyTree, CASES). Desugaring and typing this yields a closure
+ * node
+ *
+ * { def $anonfun(x: Throwable) = x match CASES; Closure(Nil, $anonfun) }
+ *
+ * At some later stage when we normalize the try we can revert this to
+ *
+ * Match(EmptyTree, CASES)
+ *
+ * or else if stack is non-empty
+ *
+ * Match(EmptyTree, <case x: Throwable => $anonfun(x)>)
+ */
+ case class Try[-T >: Untyped] private[ast] (expr: Tree[T], cases: List[CaseDef[T]], finalizer: Tree[T])
+ extends TermTree[T] {
+ type ThisTree[-T >: Untyped] = Try[T]
+ }
+
+ /** Seq(elems)
+ * @param tpt The element type of the sequence.
+ */
+ case class SeqLiteral[-T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])
+ extends Tree[T] {
+ type ThisTree[-T >: Untyped] = SeqLiteral[T]
+ }
+
+ /** Array(elems) */
+ class JavaSeqLiteral[T >: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])
+ extends SeqLiteral(elems, elemtpt) {
+ override def toString = s"JavaSeqLiteral($elems, $elemtpt)"
+ }
+
+ /** A tree representing inlined code.
+ *
+ * @param call Info about the original call that was inlined
+ * Until PostTyper, this is the full call, afterwards only
+ * a reference to the toplevel class from which the call was inlined.
+ * @param bindings Bindings for proxies to be used in the inlined code
+ * @param expansion The inlined tree, minus bindings.
+ *
+ * The full inlined code is equivalent to
+ *
+ * { bindings; expansion }
+ *
+ * The reason to keep `bindings` separate is because they are typed in a
+ * different context: `bindings` represent the arguments to the inlined
+ * call, whereas `expansion` represents the body of the inlined function.
+ */
+ case class Inlined[-T >: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])
+ extends Tree[T] {
+ type ThisTree[-T >: Untyped] = Inlined[T]
+ }
+
+ /** A type tree that represents an existing or inferred type */
+ case class TypeTree[-T >: Untyped] ()
+ extends DenotingTree[T] with TypTree[T] {
+ type ThisTree[-T >: Untyped] = TypeTree[T]
+ override def isEmpty = !hasType
+ override def toString =
+ s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
+ }
+
+ /** ref.type */
+ case class SingletonTypeTree[-T >: Untyped] private[ast] (ref: Tree[T])
+ extends DenotingTree[T] with TypTree[T] {
+ type ThisTree[-T >: Untyped] = SingletonTypeTree[T]
+ }
+
+ /** left & right */
+ case class AndTypeTree[-T >: Untyped] private[ast] (left: Tree[T], right: Tree[T])
+ extends TypTree[T] {
+ type ThisTree[-T >: Untyped] = AndTypeTree[T]
+ }
+
+ /** left | right */
+ case class OrTypeTree[-T >: Untyped] private[ast] (left: Tree[T], right: Tree[T])
+ extends TypTree[T] {
+ type ThisTree[-T >: Untyped] = OrTypeTree[T]
+ }
+
+ /** tpt { refinements } */
+ case class RefinedTypeTree[-T >: Untyped] private[ast] (tpt: Tree[T], refinements: List[Tree[T]])
+ extends ProxyTree[T] with TypTree[T] {
+ type ThisTree[-T >: Untyped] = RefinedTypeTree[T]
+ def forwardTo = tpt
+ }
+
+ /** tpt[args] */
+ case class AppliedTypeTree[-T >: Untyped] private[ast] (tpt: Tree[T], args: List[Tree[T]])
+ extends ProxyTree[T] with TypTree[T] {
+ type ThisTree[-T >: Untyped] = AppliedTypeTree[T]
+ def forwardTo = tpt
+ }
+
+ /** [typeparams] -> tpt */
+ case class PolyTypeTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T])
+ extends TypTree[T] {
+ type ThisTree[-T >: Untyped] = PolyTypeTree[T]
+ }
+
+ /** => T */
+ case class ByNameTypeTree[-T >: Untyped] private[ast] (result: Tree[T])
+ extends TypTree[T] {
+ type ThisTree[-T >: Untyped] = ByNameTypeTree[T]
+ }
+
+ /** >: lo <: hi */
+ case class TypeBoundsTree[-T >: Untyped] private[ast] (lo: Tree[T], hi: Tree[T])
+ extends TypTree[T] {
+ type ThisTree[-T >: Untyped] = TypeBoundsTree[T]
+ }
+
+ /** name @ body */
+ case class Bind[-T >: Untyped] private[ast] (name: Name, body: Tree[T])
+ extends NameTree[T] with DefTree[T] with PatternTree[T] {
+ type ThisTree[-T >: Untyped] = Bind[T]
+ override def isType = name.isTypeName
+ override def isTerm = name.isTermName
+ }
+
+ /** tree_1 | ... | tree_n */
+ case class Alternative[-T >: Untyped] private[ast] (trees: List[Tree[T]])
+ extends PatternTree[T] {
+ type ThisTree[-T >: Untyped] = Alternative[T]
+ }
+
+ /** The typed translation of `extractor(patterns)` in a pattern. The translation has the following
+ * components:
+ *
+ * @param fun is `extractor.unapply` (or, for backwards compatibility, `extractor.unapplySeq`)
+ * possibly with type parameters
+ * @param implicits Any implicit parameters passed to the unapply after the selector
+ * @param patterns The argument patterns in the pattern match.
+ *
+ * It is typed with same type as first `fun` argument
+ * Given a match selector `sel` a pattern UnApply(fun, implicits, patterns) is roughly translated as follows
+ *
+ * val result = fun(sel)(implicits)
+ * if (result.isDefined) "match patterns against result"
+ */
+ case class UnApply[-T >: Untyped] private[ast] (fun: Tree[T], implicits: List[Tree[T]], patterns: List[Tree[T]])
+ extends PatternTree[T] {
+ type ThisTree[-T >: Untyped] = UnApply[T]
+ }
+
+ /** mods val name: tpt = rhs */
+ case class ValDef[-T >: Untyped] private[ast] (name: TermName, tpt: Tree[T], private var preRhs: LazyTree)
+ extends ValOrDefDef[T] {
+ type ThisTree[-T >: Untyped] = ValDef[T]
+ assert(isEmpty || tpt != genericEmptyTree)
+ def unforced = preRhs
+ protected def force(x: AnyRef) = preRhs = x
+ }
+
+ /** mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs */
+ case class DefDef[-T >: Untyped] private[ast] (name: TermName, tparams: List[TypeDef[T]],
+ vparamss: List[List[ValDef[T]]], tpt: Tree[T], private var preRhs: LazyTree)
+ extends ValOrDefDef[T] {
+ type ThisTree[-T >: Untyped] = DefDef[T]
+ assert(tpt != genericEmptyTree)
+ def unforced = preRhs
+ protected def force(x: AnyRef) = preRhs = x
+ }
+
+ /** mods class name template or
+ * mods trait name template or
+ * mods type name = rhs or
+ * mods type name >: lo <: hi, if rhs = TypeBoundsTree(lo, hi) & (lo ne hi)
+ */
+ case class TypeDef[-T >: Untyped] private[ast] (name: TypeName, rhs: Tree[T])
+ extends MemberDef[T] {
+ type ThisTree[-T >: Untyped] = TypeDef[T]
+
+ /** Is this a definition of a class? */
+ def isClassDef = rhs.isInstanceOf[Template[_]]
+ }
+
+ /** extends parents { self => body } */
+ case class Template[-T >: Untyped] private[ast] (constr: DefDef[T], parents: List[Tree[T]], self: ValDef[T], private var preBody: LazyTreeList)
+ extends DefTree[T] with WithLazyField[List[Tree[T]]] {
+ type ThisTree[-T >: Untyped] = Template[T]
+ def unforcedBody = unforced
+ def unforced = preBody
+ protected def force(x: AnyRef) = preBody = x
+ def body(implicit ctx: Context): List[Tree[T]] = forceIfLazy
+ }
+
+ /** import expr.selectors
+ * where a selector is either an untyped `Ident`, `name` or
+ * an untyped thicket consisting of `name` and `rename`.
+ */
+ case class Import[-T >: Untyped] private[ast] (expr: Tree[T], selectors: List[Tree[Untyped]])
+ extends DenotingTree[T] {
+ type ThisTree[-T >: Untyped] = Import[T]
+ }
+
+ /** package pid { stats } */
+ case class PackageDef[-T >: Untyped] private[ast] (pid: RefTree[T], stats: List[Tree[T]])
+ extends ProxyTree[T] {
+ type ThisTree[-T >: Untyped] = PackageDef[T]
+ def forwardTo = pid
+ }
+
+ /** arg @annot */
+ case class Annotated[-T >: Untyped] private[ast] (arg: Tree[T], annot: Tree[T])
+ extends ProxyTree[T] {
+ type ThisTree[-T >: Untyped] = Annotated[T]
+ def forwardTo = arg
+ }
+
+ trait WithoutTypeOrPos[-T >: Untyped] extends Tree[T] {
+ override def tpe: T @uncheckedVariance = NoType.asInstanceOf[T]
+ override def withTypeUnchecked(tpe: Type) = this.asInstanceOf[ThisTree[Type]]
+ override def pos = NoPosition
+ override def setPos(pos: Position) = {}
+ }
+
+ /** Temporary class that results from translation of ModuleDefs
+ * (and possibly other statements).
+ * The contained trees will be integrated when transformed with
+ * a `transform(List[Tree])` call.
+ */
+ case class Thicket[-T >: Untyped](trees: List[Tree[T]])
+ extends Tree[T] with WithoutTypeOrPos[T] {
+ type ThisTree[-T >: Untyped] = Thicket[T]
+ override def isEmpty: Boolean = trees.isEmpty
+ override def toList: List[Tree[T]] = flatten(trees)
+ override def toString = if (isEmpty) "EmptyTree" else "Thicket(" + trees.mkString(", ") + ")"
+ override def withPos(pos: Position): this.type = {
+ val newTrees = trees.map(_.withPos(pos))
+ new Thicket[T](newTrees).asInstanceOf[this.type]
+ }
+ override def pos = (NoPosition /: trees) ((pos, t) => pos union t.pos)
+ override def foreachInThicket(op: Tree[T] => Unit): Unit =
+ trees foreach (_.foreachInThicket(op))
+ }
+
+ class EmptyValDef[T >: Untyped] extends ValDef[T](
+ nme.WILDCARD, genericEmptyTree[T], genericEmptyTree[T]) with WithoutTypeOrPos[T] {
+ override def isEmpty: Boolean = true
+ setMods(untpd.Modifiers(PrivateLocal))
+ }
+
+ @sharable val theEmptyTree: Thicket[Type] = Thicket(Nil)
+ @sharable val theEmptyValDef = new EmptyValDef[Type]
+
+ def genericEmptyValDef[T >: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]]
+ def genericEmptyTree[T >: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]]
+
+ def flatten[T >: Untyped](trees: List[Tree[T]]): List[Tree[T]] = {
+ var buf: ListBuffer[Tree[T]] = null
+ var xs = trees
+ while (xs.nonEmpty) {
+ xs.head match {
+ case Thicket(elems) =>
+ if (buf == null) {
+ buf = new ListBuffer
+ var ys = trees
+ while (ys ne xs) {
+ buf += ys.head
+ ys = ys.tail
+ }
+ }
+ for (elem <- elems) {
+ assert(!elem.isInstanceOf[Thicket[_]])
+ buf += elem
+ }
+ case tree =>
+ if (buf != null) buf += tree
+ }
+ xs = xs.tail
+ }
+ if (buf != null) buf.toList else trees
+ }
+
+ // ----- Lazy trees and tree sequences
+
+ /** A tree that can have a lazy field
+ * The field is represented by some private `var` which is
+ * proxied `unforced` and `force`. Forcing the field will
+ * set the `var` to the underlying value.
+ */
+ trait WithLazyField[+T <: AnyRef] {
+ def unforced: AnyRef
+ protected def force(x: AnyRef): Unit
+ def forceIfLazy(implicit ctx: Context): T = unforced match {
+ case lzy: Lazy[T] =>
+ val x = lzy.complete
+ force(x)
+ x
+ case x: T @ unchecked => x
+ }
+ }
+
+ /** A base trait for lazy tree fields.
+ * These can be instantiated with Lazy instances which
+ * can delay tree construction until the field is first demanded.
+ */
+ trait Lazy[T <: AnyRef] {
+ def complete(implicit ctx: Context): T
+ }
+
+ // ----- Generic Tree Instances, inherited from `tpt` and `untpd`.
+
+ abstract class Instance[T >: Untyped <: Type] extends DotClass { inst =>
+
+ type Tree = Trees.Tree[T]
+ type TypTree = Trees.TypTree[T]
+ type TermTree = Trees.TermTree[T]
+ type PatternTree = Trees.PatternTree[T]
+ type DenotingTree = Trees.DenotingTree[T]
+ type ProxyTree = Trees.ProxyTree[T]
+ type NameTree = Trees.NameTree[T]
+ type RefTree = Trees.RefTree[T]
+ type DefTree = Trees.DefTree[T]
+ type MemberDef = Trees.MemberDef[T]
+ type ValOrDefDef = Trees.ValOrDefDef[T]
+
+ type Ident = Trees.Ident[T]
+ type BackquotedIdent = Trees.BackquotedIdent[T]
+ type Select = Trees.Select[T]
+ type SelectWithSig = Trees.SelectWithSig[T]
+ type This = Trees.This[T]
+ type Super = Trees.Super[T]
+ type Apply = Trees.Apply[T]
+ type TypeApply = Trees.TypeApply[T]
+ type Literal = Trees.Literal[T]
+ type New = Trees.New[T]
+ type Typed = Trees.Typed[T]
+ type NamedArg = Trees.NamedArg[T]
+ type Assign = Trees.Assign[T]
+ type Block = Trees.Block[T]
+ type If = Trees.If[T]
+ type Closure = Trees.Closure[T]
+ type Match = Trees.Match[T]
+ type CaseDef = Trees.CaseDef[T]
+ type Return = Trees.Return[T]
+ type Try = Trees.Try[T]
+ type SeqLiteral = Trees.SeqLiteral[T]
+ type JavaSeqLiteral = Trees.JavaSeqLiteral[T]
+ type Inlined = Trees.Inlined[T]
+ type TypeTree = Trees.TypeTree[T]
+ type SingletonTypeTree = Trees.SingletonTypeTree[T]
+ type AndTypeTree = Trees.AndTypeTree[T]
+ type OrTypeTree = Trees.OrTypeTree[T]
+ type RefinedTypeTree = Trees.RefinedTypeTree[T]
+ type AppliedTypeTree = Trees.AppliedTypeTree[T]
+ type PolyTypeTree = Trees.PolyTypeTree[T]
+ type ByNameTypeTree = Trees.ByNameTypeTree[T]
+ type TypeBoundsTree = Trees.TypeBoundsTree[T]
+ type Bind = Trees.Bind[T]
+ type Alternative = Trees.Alternative[T]
+ type UnApply = Trees.UnApply[T]
+ type ValDef = Trees.ValDef[T]
+ type DefDef = Trees.DefDef[T]
+ type TypeDef = Trees.TypeDef[T]
+ type Template = Trees.Template[T]
+ type Import = Trees.Import[T]
+ type PackageDef = Trees.PackageDef[T]
+ type Annotated = Trees.Annotated[T]
+ type Thicket = Trees.Thicket[T]
+
+ @sharable val EmptyTree: Thicket = genericEmptyTree
+ @sharable val EmptyValDef: ValDef = genericEmptyValDef
+
+ // ----- Auxiliary creation methods ------------------
+
+ def Thicket(trees: List[Tree]): Thicket = new Thicket(trees)
+ def Thicket(): Thicket = EmptyTree
+ def Thicket(x1: Tree, x2: Tree): Thicket = Thicket(x1 :: x2 :: Nil)
+ def Thicket(x1: Tree, x2: Tree, x3: Tree): Thicket = Thicket(x1 :: x2 :: x3 :: Nil)
+ def flatTree(xs: List[Tree]): Tree = flatten(xs) match {
+ case x :: Nil => x
+ case ys => Thicket(ys)
+ }
+
+ // ----- Helper classes for copying, transforming, accumulating -----------------
+
+ val cpy: TreeCopier
+
+ /** A class for copying trees. The copy methods avoid creating a new tree
+ * If all arguments stay the same.
+ *
+ * Note: Some of the copy methods take a context.
+ * These are exactly those methods that are overridden in TypedTreeCopier
+ * so that they selectively retype themselves. Retyping needs a context.
+ */
+ abstract class TreeCopier {
+
+ def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[T]
+ def postProcess(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[T]
+
+ def finalize(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] =
+ postProcess(tree, copied withPos tree.pos)
+
+ def finalize(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[T] =
+ postProcess(tree, copied withPos tree.pos)
+
+ def Ident(tree: Tree)(name: Name): Ident = tree match {
+ case tree: BackquotedIdent =>
+ if (name == tree.name) tree
+ else finalize(tree, new BackquotedIdent(name))
+ case tree: Ident if name == tree.name => tree
+ case _ => finalize(tree, untpd.Ident(name))
+ }
+ def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = tree match {
+ case tree: SelectWithSig =>
+ if ((qualifier eq tree.qualifier) && (name == tree.name)) tree
+ else finalize(tree, new SelectWithSig(qualifier, name, tree.sig))
+ case tree: Select if (qualifier eq tree.qualifier) && (name == tree.name) => tree
+ case _ => finalize(tree, untpd.Select(qualifier, name))
+ }
+ def This(tree: Tree)(qual: untpd.Ident): This = tree match {
+ case tree: This if qual eq tree.qual => tree
+ case _ => finalize(tree, untpd.This(qual))
+ }
+ def Super(tree: Tree)(qual: Tree, mix: untpd.Ident): Super = tree match {
+ case tree: Super if (qual eq tree.qual) && (mix eq tree.mix) => tree
+ case _ => finalize(tree, untpd.Super(qual, mix))
+ }
+ def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = tree match {
+ case tree: Apply if (fun eq tree.fun) && (args eq tree.args) => tree
+ case _ => finalize(tree, untpd.Apply(fun, args))
+ }
+ def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = tree match {
+ case tree: TypeApply if (fun eq tree.fun) && (args eq tree.args) => tree
+ case _ => finalize(tree, untpd.TypeApply(fun, args))
+ }
+ def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal = tree match {
+ case tree: Literal if const == tree.const => tree
+ case _ => finalize(tree, untpd.Literal(const))
+ }
+ def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New = tree match {
+ case tree: New if tpt eq tree.tpt => tree
+ case _ => finalize(tree, untpd.New(tpt))
+ }
+ def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = tree match {
+ case tree: Typed if (expr eq tree.expr) && (tpt eq tree.tpt) => tree
+ case _ => finalize(tree, untpd.Typed(expr, tpt))
+ }
+ def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = tree match {
+ case tree: NamedArg if (name == tree.name) && (arg eq tree.arg) => tree
+ case _ => finalize(tree, untpd.NamedArg(name, arg))
+ }
+ def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = tree match {
+ case tree: Assign if (lhs eq tree.lhs) && (rhs eq tree.rhs) => tree
+ case _ => finalize(tree, untpd.Assign(lhs, rhs))
+ }
+ def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = tree match {
+ case tree: Block if (stats eq tree.stats) && (expr eq tree.expr) => tree
+ case _ => finalize(tree, untpd.Block(stats, expr))
+ }
+ def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = tree match {
+ case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree
+ case _ => finalize(tree, untpd.If(cond, thenp, elsep))
+ }
+ def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = tree match {
+ case tree: Closure if (env eq tree.env) && (meth eq tree.meth) && (tpt eq tree.tpt) => tree
+ case _ => finalize(tree, untpd.Closure(env, meth, tpt))
+ }
+ def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = tree match {
+ case tree: Match if (selector eq tree.selector) && (cases eq tree.cases) => tree
+ case _ => finalize(tree, untpd.Match(selector, cases))
+ }
+ def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = tree match {
+ case tree: CaseDef if (pat eq tree.pat) && (guard eq tree.guard) && (body eq tree.body) => tree
+ case _ => finalize(tree, untpd.CaseDef(pat, guard, body))
+ }
+ def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = tree match {
+ case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree
+ case _ => finalize(tree, untpd.Return(expr, from))
+ }
+ def Try(tree: Tree)(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try = tree match {
+ case tree: Try if (expr eq tree.expr) && (cases eq tree.cases) && (finalizer eq tree.finalizer) => tree
+ case _ => finalize(tree, untpd.Try(expr, cases, finalizer))
+ }
+ def SeqLiteral(tree: Tree)(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = tree match {
+ case tree: JavaSeqLiteral =>
+ if ((elems eq tree.elems) && (elemtpt eq tree.elemtpt)) tree
+ else finalize(tree, new JavaSeqLiteral(elems, elemtpt))
+ case tree: SeqLiteral if (elems eq tree.elems) && (elemtpt eq tree.elemtpt) => tree
+ case _ => finalize(tree, untpd.SeqLiteral(elems, elemtpt))
+ }
+ def Inlined(tree: Tree)(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined = tree match {
+ case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree
+ case _ => finalize(tree, untpd.Inlined(call, bindings, expansion))
+ }
+ def SingletonTypeTree(tree: Tree)(ref: Tree): SingletonTypeTree = tree match {
+ case tree: SingletonTypeTree if ref eq tree.ref => tree
+ case _ => finalize(tree, untpd.SingletonTypeTree(ref))
+ }
+ def AndTypeTree(tree: Tree)(left: Tree, right: Tree): AndTypeTree = tree match {
+ case tree: AndTypeTree if (left eq tree.left) && (right eq tree.right) => tree
+ case _ => finalize(tree, untpd.AndTypeTree(left, right))
+ }
+ def OrTypeTree(tree: Tree)(left: Tree, right: Tree): OrTypeTree = tree match {
+ case tree: OrTypeTree if (left eq tree.left) && (right eq tree.right) => tree
+ case _ => finalize(tree, untpd.OrTypeTree(left, right))
+ }
+ def RefinedTypeTree(tree: Tree)(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = tree match {
+ case tree: RefinedTypeTree if (tpt eq tree.tpt) && (refinements eq tree.refinements) => tree
+ case _ => finalize(tree, untpd.RefinedTypeTree(tpt, refinements))
+ }
+ def AppliedTypeTree(tree: Tree)(tpt: Tree, args: List[Tree]): AppliedTypeTree = tree match {
+ case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree
+ case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args))
+ }
+ def PolyTypeTree(tree: Tree)(tparams: List[TypeDef], body: Tree): PolyTypeTree = tree match {
+ case tree: PolyTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree
+ case _ => finalize(tree, untpd.PolyTypeTree(tparams, body))
+ }
+ def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match {
+ case tree: ByNameTypeTree if result eq tree.result => tree
+ case _ => finalize(tree, untpd.ByNameTypeTree(result))
+ }
+ def TypeBoundsTree(tree: Tree)(lo: Tree, hi: Tree): TypeBoundsTree = tree match {
+ case tree: TypeBoundsTree if (lo eq tree.lo) && (hi eq tree.hi) => tree
+ case _ => finalize(tree, untpd.TypeBoundsTree(lo, hi))
+ }
+ def Bind(tree: Tree)(name: Name, body: Tree): Bind = tree match {
+ case tree: Bind if (name eq tree.name) && (body eq tree.body) => tree
+ case _ => finalize(tree, untpd.Bind(name, body))
+ }
+ def Alternative(tree: Tree)(trees: List[Tree]): Alternative = tree match {
+ case tree: Alternative if trees eq tree.trees => tree
+ case _ => finalize(tree, untpd.Alternative(trees))
+ }
+ def UnApply(tree: Tree)(fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = tree match {
+ case tree: UnApply if (fun eq tree.fun) && (implicits eq tree.implicits) && (patterns eq tree.patterns) => tree
+ case _ => finalize(tree, untpd.UnApply(fun, implicits, patterns))
+ }
+ def ValDef(tree: Tree)(name: TermName, tpt: Tree, rhs: LazyTree): ValDef = tree match {
+ case tree: ValDef if (name == tree.name) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree
+ case _ => finalize(tree, untpd.ValDef(name, tpt, rhs))
+ }
+ def DefDef(tree: Tree)(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = tree match {
+ case tree: DefDef if (name == tree.name) && (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree
+ case _ => finalize(tree, untpd.DefDef(name, tparams, vparamss, tpt, rhs))
+ }
+ def TypeDef(tree: Tree)(name: TypeName, rhs: Tree): TypeDef = tree match {
+ case tree: TypeDef if (name == tree.name) && (rhs eq tree.rhs) => tree
+ case _ => finalize(tree, untpd.TypeDef(name, rhs))
+ }
+ def Template(tree: Tree)(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList): Template = tree match {
+ case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (self eq tree.self) && (body eq tree.unforcedBody) => tree
+ case _ => finalize(tree, untpd.Template(constr, parents, self, body))
+ }
+ def Import(tree: Tree)(expr: Tree, selectors: List[untpd.Tree]): Import = tree match {
+ case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
+ case _ => finalize(tree, untpd.Import(expr, selectors))
+ }
+ def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree]): PackageDef = tree match {
+ case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree
+ case _ => finalize(tree, untpd.PackageDef(pid, stats))
+ }
+ def Annotated(tree: Tree)(arg: Tree, annot: Tree)(implicit ctx: Context): Annotated = tree match {
+ case tree: Annotated if (arg eq tree.arg) && (annot eq tree.annot) => tree
+ case _ => finalize(tree, untpd.Annotated(arg, annot))
+ }
+ def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match {
+ case tree: Thicket if trees eq tree.trees => tree
+ case _ => finalize(tree, untpd.Thicket(trees))
+ }
+
+ // Copier methods with default arguments; these demand that the original tree
+ // is of the same class as the copy. We only include trees with more than 2 elements here.
+ def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If =
+ If(tree: Tree)(cond, thenp, elsep)
+ def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure =
+ Closure(tree: Tree)(env, meth, tpt)
+ def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef =
+ CaseDef(tree: Tree)(pat, guard, body)
+ def Try(tree: Try)(expr: Tree = tree.expr, cases: List[CaseDef] = tree.cases, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try =
+ Try(tree: Tree)(expr, cases, finalizer)
+ def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply =
+ UnApply(tree: Tree)(fun, implicits, patterns)
+ def ValDef(tree: ValDef)(name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): ValDef =
+ ValDef(tree: Tree)(name, tpt, rhs)
+ def DefDef(tree: DefDef)(name: TermName = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): DefDef =
+ DefDef(tree: Tree)(name, tparams, vparamss, tpt, rhs)
+ def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs): TypeDef =
+ TypeDef(tree: Tree)(name, rhs)
+ def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template =
+ Template(tree: Tree)(constr, parents, self, body)
+ }
+
+ abstract class TreeMap(val cpy: TreeCopier = inst.cpy) {
+
+ def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case Ident(name) =>
+ tree
+ case Select(qualifier, name) =>
+ cpy.Select(tree)(transform(qualifier), name)
+ case This(qual) =>
+ tree
+ case Super(qual, mix) =>
+ cpy.Super(tree)(transform(qual), mix)
+ case Apply(fun, args) =>
+ cpy.Apply(tree)(transform(fun), transform(args))
+ case TypeApply(fun, args) =>
+ cpy.TypeApply(tree)(transform(fun), transform(args))
+ case Literal(const) =>
+ tree
+ case New(tpt) =>
+ cpy.New(tree)(transform(tpt))
+ case Typed(expr, tpt) =>
+ cpy.Typed(tree)(transform(expr), transform(tpt))
+ case NamedArg(name, arg) =>
+ cpy.NamedArg(tree)(name, transform(arg))
+ case Assign(lhs, rhs) =>
+ cpy.Assign(tree)(transform(lhs), transform(rhs))
+ case Block(stats, expr) =>
+ cpy.Block(tree)(transformStats(stats), transform(expr))
+ case If(cond, thenp, elsep) =>
+ cpy.If(tree)(transform(cond), transform(thenp), transform(elsep))
+ case Closure(env, meth, tpt) =>
+ cpy.Closure(tree)(transform(env), transform(meth), transform(tpt))
+ case Match(selector, cases) =>
+ cpy.Match(tree)(transform(selector), transformSub(cases))
+ case CaseDef(pat, guard, body) =>
+ cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body))
+ case Return(expr, from) =>
+ cpy.Return(tree)(transform(expr), transformSub(from))
+ case Try(block, cases, finalizer) =>
+ cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer))
+ case SeqLiteral(elems, elemtpt) =>
+ cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt))
+ case Inlined(call, bindings, expansion) =>
+ cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion))
+ case TypeTree() =>
+ tree
+ case SingletonTypeTree(ref) =>
+ cpy.SingletonTypeTree(tree)(transform(ref))
+ case AndTypeTree(left, right) =>
+ cpy.AndTypeTree(tree)(transform(left), transform(right))
+ case OrTypeTree(left, right) =>
+ cpy.OrTypeTree(tree)(transform(left), transform(right))
+ case RefinedTypeTree(tpt, refinements) =>
+ cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements))
+ case AppliedTypeTree(tpt, args) =>
+ cpy.AppliedTypeTree(tree)(transform(tpt), transform(args))
+ case PolyTypeTree(tparams, body) =>
+ cpy.PolyTypeTree(tree)(transformSub(tparams), transform(body))
+ case ByNameTypeTree(result) =>
+ cpy.ByNameTypeTree(tree)(transform(result))
+ case TypeBoundsTree(lo, hi) =>
+ cpy.TypeBoundsTree(tree)(transform(lo), transform(hi))
+ case Bind(name, body) =>
+ cpy.Bind(tree)(name, transform(body))
+ case Alternative(trees) =>
+ cpy.Alternative(tree)(transform(trees))
+ case UnApply(fun, implicits, patterns) =>
+ cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns))
+ case EmptyValDef =>
+ tree
+ case tree @ ValDef(name, tpt, _) =>
+ val tpt1 = transform(tpt)
+ val rhs1 = transform(tree.rhs)
+ cpy.ValDef(tree)(name, tpt1, rhs1)
+ case tree @ DefDef(name, tparams, vparamss, tpt, _) =>
+ cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs))
+ case tree @ TypeDef(name, rhs) =>
+ cpy.TypeDef(tree)(name, transform(rhs))
+ case tree @ Template(constr, parents, self, _) =>
+ cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(tree.body))
+ case Import(expr, selectors) =>
+ cpy.Import(tree)(transform(expr), selectors)
+ case PackageDef(pid, stats) =>
+ cpy.PackageDef(tree)(transformSub(pid), transformStats(stats))
+ case Annotated(arg, annot) =>
+ cpy.Annotated(tree)(transform(arg), transform(annot))
+ case Thicket(trees) =>
+ val trees1 = transform(trees)
+ if (trees1 eq trees) tree else Thicket(trees1)
+ }
+
+ def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] =
+ transform(trees)
+ def transform(trees: List[Tree])(implicit ctx: Context): List[Tree] =
+ flatten(trees mapConserve (transform(_)))
+ def transformSub[Tr <: Tree](tree: Tr)(implicit ctx: Context): Tr =
+ transform(tree).asInstanceOf[Tr]
+ def transformSub[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] =
+ transform(trees).asInstanceOf[List[Tr]]
+ }
+
+ abstract class TreeAccumulator[X] {
+ def apply(x: X, tree: Tree)(implicit ctx: Context): X
+ def apply(x: X, trees: Traversable[Tree])(implicit ctx: Context): X = (x /: trees)(apply)
+ def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = {
+ def localCtx =
+ if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx
+ tree match {
+ case Ident(name) =>
+ x
+ case Select(qualifier, name) =>
+ this(x, qualifier)
+ case This(qual) =>
+ x
+ case Super(qual, mix) =>
+ this(x, qual)
+ case Apply(fun, args) =>
+ this(this(x, fun), args)
+ case TypeApply(fun, args) =>
+ this(this(x, fun), args)
+ case Literal(const) =>
+ x
+ case New(tpt) =>
+ this(x, tpt)
+ case Typed(expr, tpt) =>
+ this(this(x, expr), tpt)
+ case NamedArg(name, arg) =>
+ this(x, arg)
+ case Assign(lhs, rhs) =>
+ this(this(x, lhs), rhs)
+ case Block(stats, expr) =>
+ this(this(x, stats), expr)
+ case If(cond, thenp, elsep) =>
+ this(this(this(x, cond), thenp), elsep)
+ case Closure(env, meth, tpt) =>
+ this(this(this(x, env), meth), tpt)
+ case Match(selector, cases) =>
+ this(this(x, selector), cases)
+ case CaseDef(pat, guard, body) =>
+ this(this(this(x, pat), guard), body)
+ case Return(expr, from) =>
+ this(this(x, expr), from)
+ case Try(block, handler, finalizer) =>
+ this(this(this(x, block), handler), finalizer)
+ case SeqLiteral(elems, elemtpt) =>
+ this(this(x, elems), elemtpt)
+ case Inlined(call, bindings, expansion) =>
+ this(this(x, bindings), expansion)
+ case TypeTree() =>
+ x
+ case SingletonTypeTree(ref) =>
+ this(x, ref)
+ case AndTypeTree(left, right) =>
+ this(this(x, left), right)
+ case OrTypeTree(left, right) =>
+ this(this(x, left), right)
+ case RefinedTypeTree(tpt, refinements) =>
+ this(this(x, tpt), refinements)
+ case AppliedTypeTree(tpt, args) =>
+ this(this(x, tpt), args)
+ case PolyTypeTree(tparams, body) =>
+ implicit val ctx: Context = localCtx
+ this(this(x, tparams), body)
+ case ByNameTypeTree(result) =>
+ this(x, result)
+ case TypeBoundsTree(lo, hi) =>
+ this(this(x, lo), hi)
+ case Bind(name, body) =>
+ this(x, body)
+ case Alternative(trees) =>
+ this(x, trees)
+ case UnApply(fun, implicits, patterns) =>
+ this(this(this(x, fun), implicits), patterns)
+ case tree @ ValDef(name, tpt, _) =>
+ implicit val ctx: Context = localCtx
+ this(this(x, tpt), tree.rhs)
+ case tree @ DefDef(name, tparams, vparamss, tpt, _) =>
+ implicit val ctx: Context = localCtx
+ this(this((this(x, tparams) /: vparamss)(apply), tpt), tree.rhs)
+ case TypeDef(name, rhs) =>
+ implicit val ctx: Context = localCtx
+ this(x, rhs)
+ case tree @ Template(constr, parents, self, _) =>
+ this(this(this(this(x, constr), parents), self), tree.body)
+ case Import(expr, selectors) =>
+ this(x, expr)
+ case PackageDef(pid, stats) =>
+ this(this(x, pid), stats)(localCtx)
+ case Annotated(arg, annot) =>
+ this(this(x, arg), annot)
+ case Thicket(ts) =>
+ this(x, ts)
+ }
+ }
+ }
+
+ abstract class TreeTraverser extends TreeAccumulator[Unit] {
+ def traverse(tree: Tree)(implicit ctx: Context): Unit
+ def apply(x: Unit, tree: Tree)(implicit ctx: Context) = traverse(tree)
+ protected def traverseChildren(tree: Tree)(implicit ctx: Context) = foldOver((), tree)
+ }
+
+ /** Fold `f` over all tree nodes, in depth-first, prefix order */
+ class DeepFolder[X](f: (X, Tree) => X) extends TreeAccumulator[X] {
+ def apply(x: X, tree: Tree)(implicit ctx: Context): X = foldOver(f(x, tree), tree)
+ }
+
+ /** Fold `f` over all tree nodes, in depth-first, prefix order, but don't visit
+ * subtrees where `f` returns a different result for the root, i.e. `f(x, root) ne x`.
+ */
+ class ShallowFolder[X](f: (X, Tree) => X) extends TreeAccumulator[X] {
+ def apply(x: X, tree: Tree)(implicit ctx: Context): X = {
+ val x1 = f(x, tree)
+ if (x1.asInstanceOf[AnyRef] ne x1.asInstanceOf[AnyRef]) x1
+ else foldOver(x1, tree)
+ }
+ }
+
+ def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[T] = {
+ tree match {
+ case tree: Ident => cpy.Ident(tree)(newName)
+ case tree: Select => cpy.Select(tree)(tree.qualifier, newName)
+ case tree: Bind => cpy.Bind(tree)(newName, tree.body)
+ case tree: ValDef => cpy.ValDef(tree)(name = newName.asTermName)
+ case tree: DefDef => cpy.DefDef(tree)(name = newName.asTermName)
+ case tree: TypeDef => cpy.TypeDef(tree)(name = newName.asTypeName)
+ }
+ }.asInstanceOf[tree.ThisTree[T]]
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala
new file mode 100644
index 000000000..44e1cf188
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala
@@ -0,0 +1,952 @@
+package dotty.tools
+package dotc
+package ast
+
+import dotty.tools.dotc.transform.{ExplicitOuter, Erasure}
+import dotty.tools.dotc.typer.ProtoTypes.FunProtoTyped
+import transform.SymUtils._
+import core._
+import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._
+import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._
+import Denotations._, Decorators._, DenotTransformers._
+import collection.mutable
+import util.{Property, SourceFile, NoSource}
+import typer.ErrorReporting._
+
+import scala.annotation.tailrec
+import scala.io.Codec
+
+/** Some creators for typed trees */
+object tpd extends Trees.Instance[Type] with TypedTreeInfo {
+
+ private def ta(implicit ctx: Context) = ctx.typeAssigner
+
+ def Ident(tp: NamedType)(implicit ctx: Context): Ident =
+ ta.assignType(untpd.Ident(tp.name), tp)
+
+ def Select(qualifier: Tree, name: Name)(implicit ctx: Context): Select =
+ ta.assignType(untpd.Select(qualifier, name), qualifier)
+
+ def Select(qualifier: Tree, tp: NamedType)(implicit ctx: Context): Select =
+ untpd.Select(qualifier, tp.name).withType(tp)
+
+ def This(cls: ClassSymbol)(implicit ctx: Context): This =
+ untpd.This(untpd.Ident(cls.name)).withType(cls.thisType)
+
+ def Super(qual: Tree, mix: untpd.Ident, inConstrCall: Boolean, mixinClass: Symbol)(implicit ctx: Context): Super =
+ ta.assignType(untpd.Super(qual, mix), qual, inConstrCall, mixinClass)
+
+ def Super(qual: Tree, mixName: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super =
+ Super(qual, if (mixName.isEmpty) untpd.EmptyTypeIdent else untpd.Ident(mixName), inConstrCall, mixinClass)
+
+ def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply =
+ ta.assignType(untpd.Apply(fn, args), fn, args)
+
+ def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
+ ta.assignType(untpd.TypeApply(fn, args), fn, args)
+
+ def Literal(const: Constant)(implicit ctx: Context): Literal =
+ ta.assignType(untpd.Literal(const))
+
+ def unitLiteral(implicit ctx: Context): Literal =
+ Literal(Constant(()))
+
+ def New(tpt: Tree)(implicit ctx: Context): New =
+ ta.assignType(untpd.New(tpt), tpt)
+
+ def New(tp: Type)(implicit ctx: Context): New = New(TypeTree(tp))
+
+ def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed =
+ ta.assignType(untpd.Typed(expr, tpt), tpt)
+
+ def NamedArg(name: Name, arg: Tree)(implicit ctx: Context): NamedArg =
+ ta.assignType(untpd.NamedArg(name, arg), arg)
+
+ def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign =
+ ta.assignType(untpd.Assign(lhs, rhs))
+
+ def Block(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block =
+ ta.assignType(untpd.Block(stats, expr), stats, expr)
+
+ /** Join `stats` in front of `expr` creating a new block if necessary */
+ def seq(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree =
+ if (stats.isEmpty) expr
+ else expr match {
+ case Block(estats, eexpr) => cpy.Block(expr)(stats ::: estats, eexpr)
+ case _ => Block(stats, expr)
+ }
+
+ def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If =
+ ta.assignType(untpd.If(cond, thenp, elsep), thenp, elsep)
+
+ def Closure(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure =
+ ta.assignType(untpd.Closure(env, meth, tpt), meth, tpt)
+
+ /** A function def
+ *
+ * vparams => expr
+ *
+ * gets expanded to
+ *
+ * { def $anonfun(vparams) = expr; Closure($anonfun) }
+ *
+ * where the closure's type is the target type of the expression (FunctionN, unless
+ * otherwise specified).
+ */
+ def Closure(meth: TermSymbol, rhsFn: List[List[Tree]] => Tree, targs: List[Tree] = Nil, targetType: Type = NoType)(implicit ctx: Context): Block = {
+ val targetTpt = if (targetType.exists) TypeTree(targetType) else EmptyTree
+ val call =
+ if (targs.isEmpty) Ident(TermRef(NoPrefix, meth))
+ else TypeApply(Ident(TermRef(NoPrefix, meth)), targs)
+ Block(
+ DefDef(meth, rhsFn) :: Nil,
+ Closure(Nil, call, targetTpt))
+ }
+
+ def CaseDef(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef =
+ ta.assignType(untpd.CaseDef(pat, guard, body), body)
+
+ def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match =
+ ta.assignType(untpd.Match(selector, cases), cases)
+
+ def Return(expr: Tree, from: Tree)(implicit ctx: Context): Return =
+ ta.assignType(untpd.Return(expr, from))
+
+ def Try(block: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try =
+ ta.assignType(untpd.Try(block, cases, finalizer), block, cases)
+
+ def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral =
+ ta.assignType(untpd.SeqLiteral(elems, elemtpt), elems, elemtpt)
+
+ def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): JavaSeqLiteral =
+ ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt).asInstanceOf[JavaSeqLiteral]
+
+ def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined =
+ ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion)
+
+ def TypeTree(tp: Type)(implicit ctx: Context): TypeTree =
+ untpd.TypeTree().withType(tp)
+
+ def SingletonTypeTree(ref: Tree)(implicit ctx: Context): SingletonTypeTree =
+ ta.assignType(untpd.SingletonTypeTree(ref), ref)
+
+ def AndTypeTree(left: Tree, right: Tree)(implicit ctx: Context): AndTypeTree =
+ ta.assignType(untpd.AndTypeTree(left, right), left, right)
+
+ def OrTypeTree(left: Tree, right: Tree)(implicit ctx: Context): OrTypeTree =
+ ta.assignType(untpd.OrTypeTree(left, right), left, right)
+
+ def RefinedTypeTree(parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context): Tree =
+ ta.assignType(untpd.RefinedTypeTree(parent, refinements), parent, refinements, refineCls)
+
+ def AppliedTypeTree(tycon: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree =
+ ta.assignType(untpd.AppliedTypeTree(tycon, args), tycon, args)
+
+ def ByNameTypeTree(result: Tree)(implicit ctx: Context): ByNameTypeTree =
+ ta.assignType(untpd.ByNameTypeTree(result), result)
+
+ def PolyTypeTree(tparams: List[TypeDef], body: Tree)(implicit ctx: Context): PolyTypeTree =
+ ta.assignType(untpd.PolyTypeTree(tparams, body), tparams, body)
+
+ def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree =
+ ta.assignType(untpd.TypeBoundsTree(lo, hi), lo, hi)
+
+ def Bind(sym: TermSymbol, body: Tree)(implicit ctx: Context): Bind =
+ ta.assignType(untpd.Bind(sym.name, body), sym)
+
+ /** A pattern corresponding to `sym: tpe` */
+ def BindTyped(sym: TermSymbol, tpe: Type)(implicit ctx: Context): Bind =
+ Bind(sym, Typed(Underscore(tpe), TypeTree(tpe)))
+
+ def Alternative(trees: List[Tree])(implicit ctx: Context): Alternative =
+ ta.assignType(untpd.Alternative(trees), trees)
+
+ def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree], proto: Type)(implicit ctx: Context): UnApply =
+ ta.assignType(untpd.UnApply(fun, implicits, patterns), proto)
+
+ def ValDef(sym: TermSymbol, rhs: LazyTree = EmptyTree)(implicit ctx: Context): ValDef =
+ ta.assignType(untpd.ValDef(sym.name, TypeTree(sym.info), rhs), sym)
+
+ def SyntheticValDef(name: TermName, rhs: Tree)(implicit ctx: Context): ValDef =
+ ValDef(ctx.newSymbol(ctx.owner, name, Synthetic, rhs.tpe.widen, coord = rhs.pos), rhs)
+
+ def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef =
+ ta.assignType(DefDef(sym, Function.const(rhs) _), sym)
+
+ def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef =
+ polyDefDef(sym, Function.const(rhsFn))
+
+ def polyDefDef(sym: TermSymbol, rhsFn: List[Type] => List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = {
+ val (tparams, mtp) = sym.info match {
+ case tp: PolyType =>
+ val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateBounds)
+ (tparams, tp.instantiate(tparams map (_.typeRef)))
+ case tp => (Nil, tp)
+ }
+
+ def valueParamss(tp: Type): (List[List[TermSymbol]], Type) = tp match {
+ case tp @ MethodType(paramNames, paramTypes) =>
+ def valueParam(name: TermName, info: Type): TermSymbol = {
+ val maybeImplicit = if (tp.isInstanceOf[ImplicitMethodType]) Implicit else EmptyFlags
+ ctx.newSymbol(sym, name, TermParam | maybeImplicit, info)
+ }
+ val params = (paramNames, paramTypes).zipped.map(valueParam)
+ val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef)))
+ (params :: paramss, rtp)
+ case tp => (Nil, tp.widenExpr)
+ }
+ val (vparamss, rtp) = valueParamss(mtp)
+ val targs = tparams map (_.typeRef)
+ val argss = vparamss.nestedMap(vparam => Ident(vparam.termRef))
+ ta.assignType(
+ untpd.DefDef(
+ sym.name,
+ tparams map TypeDef,
+ vparamss.nestedMap(ValDef(_)),
+ TypeTree(rtp),
+ rhsFn(targs)(argss)),
+ sym)
+ }
+
+ def TypeDef(sym: TypeSymbol)(implicit ctx: Context): TypeDef =
+ ta.assignType(untpd.TypeDef(sym.name, TypeTree(sym.info)), sym)
+
+ def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(implicit ctx: Context): TypeDef = {
+ val firstParentRef :: otherParentRefs = cls.info.parents
+ val firstParent = cls.typeRef.baseTypeWithArgs(firstParentRef.symbol)
+ val superRef =
+ if (cls is Trait) TypeTree(firstParent)
+ else {
+ def isApplicable(ctpe: Type): Boolean = ctpe match {
+ case ctpe: PolyType =>
+ isApplicable(ctpe.instantiate(firstParent.argTypes))
+ case ctpe: MethodType =>
+ (superArgs corresponds ctpe.paramTypes)(_.tpe <:< _)
+ case _ =>
+ false
+ }
+ val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(constr => isApplicable(constr.info))
+ New(firstParent, constr.symbol.asTerm, superArgs)
+ }
+ val parents = superRef :: otherParentRefs.map(TypeTree(_))
+
+ val selfType =
+ if (cls.classInfo.selfInfo ne NoType) ValDef(ctx.newSelfSym(cls))
+ else EmptyValDef
+ def isOwnTypeParam(stat: Tree) =
+ (stat.symbol is TypeParam) && stat.symbol.owner == cls
+ val bodyTypeParams = body filter isOwnTypeParam map (_.symbol)
+ val newTypeParams =
+ for (tparam <- cls.typeParams if !(bodyTypeParams contains tparam))
+ yield TypeDef(tparam)
+ val findLocalDummy = new FindLocalDummyAccumulator(cls)
+ val localDummy = ((NoSymbol: Symbol) /: body)(findLocalDummy.apply)
+ .orElse(ctx.newLocalDummy(cls))
+ val impl = untpd.Template(constr, parents, selfType, newTypeParams ++ body)
+ .withType(localDummy.nonMemberTermRef)
+ ta.assignType(untpd.TypeDef(cls.name, impl), cls)
+ }
+
+ /** An anonymous class
+ *
+ * new parents { forwarders }
+ *
+ * where `forwarders` contains forwarders for all functions in `fns`.
+ * @param parents a non-empty list of class types
+ * @param fns a non-empty of functions for which forwarders should be defined in the class.
+ * The class has the same owner as the first function in `fns`.
+ * Its position is the union of all functions in `fns`.
+ */
+ def AnonClass(parents: List[Type], fns: List[TermSymbol], methNames: List[TermName])(implicit ctx: Context): Block = {
+ val owner = fns.head.owner
+ val parents1 =
+ if (parents.head.classSymbol.is(Trait)) defn.ObjectType :: parents
+ else parents
+ val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_FUN, Synthetic, parents1,
+ coord = fns.map(_.pos).reduceLeft(_ union _))
+ val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil).entered
+ def forwarder(fn: TermSymbol, name: TermName) = {
+ val fwdMeth = fn.copy(cls, name, Synthetic | Method).entered.asTerm
+ DefDef(fwdMeth, prefss => ref(fn).appliedToArgss(prefss))
+ }
+ val forwarders = (fns, methNames).zipped.map(forwarder)
+ val cdef = ClassDef(cls, DefDef(constr), forwarders)
+ Block(cdef :: Nil, New(cls.typeRef, Nil))
+ }
+
+ // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() }
+ def WhileDo(owner: Symbol, cond: Tree, body: List[Tree])(implicit ctx: Context): Tree = {
+ val sym = ctx.newSymbol(owner, nme.WHILE_PREFIX, Flags.Label | Flags.Synthetic,
+ MethodType(Nil, defn.UnitType), coord = cond.pos)
+
+ val call = Apply(ref(sym), Nil)
+ val rhs = If(cond, Block(body, call), unitLiteral)
+ Block(List(DefDef(sym, rhs)), call)
+ }
+
+ def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import =
+ ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(ctx.owner, expr))
+
+ def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef =
+ ta.assignType(untpd.PackageDef(pid, stats), pid)
+
+ def Annotated(arg: Tree, annot: Tree)(implicit ctx: Context): Annotated =
+ ta.assignType(untpd.Annotated(arg, annot), arg, annot)
+
+ def Throw(expr: Tree)(implicit ctx: Context): Tree =
+ ref(defn.throwMethod).appliedTo(expr)
+
+ // ------ Making references ------------------------------------------------------
+
+ def prefixIsElidable(tp: NamedType)(implicit ctx: Context) = {
+ val typeIsElidable = tp.prefix match {
+ case NoPrefix =>
+ true
+ case pre: ThisType =>
+ pre.cls.isStaticOwner ||
+ tp.symbol.is(ParamOrAccessor) && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls
+ // was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough
+ // and was spuriously triggered in case inner class would inherit from outer one
+ // eg anonymous TypeMap inside TypeMap.andThen
+ case pre: TermRef =>
+ pre.symbol.is(Module) && pre.symbol.isStatic
+ case _ =>
+ false
+ }
+ typeIsElidable ||
+ tp.symbol.is(JavaStatic) ||
+ tp.symbol.hasAnnotation(defn.ScalaStaticAnnot)
+ }
+
+ def needsSelect(tp: Type)(implicit ctx: Context) = tp match {
+ case tp: TermRef => !prefixIsElidable(tp)
+ case _ => false
+ }
+
+ /** A tree representing the same reference as the given type */
+ def ref(tp: NamedType)(implicit ctx: Context): Tree =
+ if (tp.isType) TypeTree(tp)
+ else if (prefixIsElidable(tp)) Ident(tp)
+ else if (tp.symbol.is(Module) && ctx.owner.isContainedIn(tp.symbol.moduleClass))
+ followOuterLinks(This(tp.symbol.moduleClass.asClass))
+ else if (tp.symbol hasAnnotation defn.ScalaStaticAnnot)
+ Ident(tp)
+ else tp.prefix match {
+ case pre: SingletonType => followOuterLinks(singleton(pre)).select(tp)
+ case pre => Select(TypeTree(pre), tp)
+ } // no checks necessary
+
+ def ref(sym: Symbol)(implicit ctx: Context): Tree =
+ ref(NamedType(sym.owner.thisType, sym.name, sym.denot))
+
+ private def followOuterLinks(t: Tree)(implicit ctx: Context) = t match {
+ case t: This if ctx.erasedTypes && !(t.symbol == ctx.owner.enclosingClass || t.symbol.isStaticOwner) =>
+ // after erasure outer paths should be respected
+ new ExplicitOuter.OuterOps(ctx).path(t.tpe.widen.classSymbol)
+ case t =>
+ t
+ }
+
+ def singleton(tp: Type)(implicit ctx: Context): Tree = tp match {
+ case tp: TermRef => ref(tp)
+ case tp: ThisType => This(tp.cls)
+ case tp: SkolemType => singleton(tp.narrow)
+ case SuperType(qual, _) => singleton(qual)
+ case ConstantType(value) => Literal(value)
+ }
+
+ /** A tree representing a `newXYZArray` operation of the right
+ * kind for the given element type in `typeArg`. No type arguments or
+ * `length` arguments are given.
+ */
+ def newArray(elemTpe: Type, returnTpe: Type, pos: Position, dims: JavaSeqLiteral)(implicit ctx: Context): Tree = {
+ val elemClass = elemTpe.classSymbol
+ def newArr =
+ ref(defn.DottyArraysModule).select(defn.newArrayMethod).withPos(pos)
+
+ if (!ctx.erasedTypes) {
+ assert(!TypeErasure.isUnboundedGeneric(elemTpe)) //needs to be done during typer. See Applications.convertNewGenericArray
+ newArr.appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos)
+ } else // after erasure
+ newArr.appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos)
+ }
+
+ // ------ Creating typed equivalents of trees that exist only in untyped form -------
+
+ /** new C(args), calling the primary constructor of C */
+ def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply =
+ New(tp, tp.typeSymbol.primaryConstructor.asTerm, args)
+
+ /** new C(args), calling given constructor `constr` of C */
+ def New(tp: Type, constr: TermSymbol, args: List[Tree])(implicit ctx: Context): Apply = {
+ val targs = tp.argTypes
+ val tycon = tp.withoutArgs(targs)
+ New(tycon)
+ .select(TermRef.withSig(tycon, constr))
+ .appliedToTypes(targs)
+ .appliedToArgs(args)
+ }
+
+ /** An object def
+ *
+ * object obs extends parents { decls }
+ *
+ * gets expanded to
+ *
+ * <module> val obj = new obj$
+ * <module> class obj$ extends parents { this: obj.type => decls }
+ *
+ * (The following no longer applies:
+ * What's interesting here is that the block is well typed
+ * (because class obj$ is hoistable), but the type of the `obj` val is
+ * not expressible. What needs to happen in general when
+ * inferring the type of a val from its RHS, is: if the type contains
+ * a class that has the val itself as owner, then that class
+ * is remapped to have the val's owner as owner. Remapping could be
+ * done by cloning the class with the new owner and substituting
+ * everywhere in the tree. We know that remapping is safe
+ * because the only way a local class can appear in the RHS of a val is
+ * by being hoisted outside of a block, and the necessary checks are
+ * done at this point already.
+ *
+ * On the other hand, for method result type inference, if the type of
+ * the RHS of a method contains a class owned by the method, this would be
+ * an error.)
+ */
+ def ModuleDef(sym: TermSymbol, body: List[Tree])(implicit ctx: Context): tpd.Thicket = {
+ val modcls = sym.moduleClass.asClass
+ val constrSym = modcls.primaryConstructor orElse ctx.newDefaultConstructor(modcls).entered
+ val constr = DefDef(constrSym.asTerm, EmptyTree)
+ val clsdef = ClassDef(modcls, constr, body)
+ val valdef = ValDef(sym, New(modcls.typeRef).select(constrSym).appliedToNone)
+ Thicket(valdef, clsdef)
+ }
+
+ /** A `_' with given type */
+ def Underscore(tp: Type)(implicit ctx: Context) = untpd.Ident(nme.WILDCARD).withType(tp)
+
+ def defaultValue(tpe: Types.Type)(implicit ctx: Context) = {
+ val tpw = tpe.widen
+
+ if (tpw isRef defn.IntClass) Literal(Constant(0))
+ else if (tpw isRef defn.LongClass) Literal(Constant(0L))
+ else if (tpw isRef defn.BooleanClass) Literal(Constant(false))
+ else if (tpw isRef defn.CharClass) Literal(Constant('\u0000'))
+ else if (tpw isRef defn.FloatClass) Literal(Constant(0f))
+ else if (tpw isRef defn.DoubleClass) Literal(Constant(0d))
+ else if (tpw isRef defn.ByteClass) Literal(Constant(0.toByte))
+ else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort))
+ else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe)
+ }
+
+ private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] {
+ def apply(sym: Symbol, tree: Tree)(implicit ctx: Context) =
+ if (sym.exists) sym
+ else if (tree.isDef) {
+ val owner = tree.symbol.owner
+ if (owner.isLocalDummy && owner.owner == cls) owner
+ else if (owner == cls) foldOver(sym, tree)
+ else sym
+ } else foldOver(sym, tree)
+ }
+
+ override val cpy = new TypedTreeCopier
+
+ class TypedTreeCopier extends TreeCopier {
+ def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] =
+ copied.withTypeUnchecked(tree.tpe)
+ def postProcess(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[Type] =
+ copied.withTypeUnchecked(tree.tpe)
+
+ override def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = {
+ val tree1 = untpd.cpy.Select(tree)(qualifier, name)
+ tree match {
+ case tree: Select if qualifier.tpe eq tree.qualifier.tpe =>
+ tree1.withTypeUnchecked(tree.tpe)
+ case _ => tree.tpe match {
+ case tpe: NamedType => tree1.withType(tpe.derivedSelect(qualifier.tpe))
+ case _ => tree1.withTypeUnchecked(tree.tpe)
+ }
+ }
+ }
+
+ override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply =
+ ta.assignType(untpd.cpy.Apply(tree)(fun, args), fun, args)
+ // Note: Reassigning the original type if `fun` and `args` have the same types as before
+ // does not work here: The computed type depends on the widened function type, not
+ // the function type itself. A treetransform may keep the function type the
+ // same but its widened type might change.
+
+ override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
+ ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args)
+ // Same remark as for Apply
+
+ override def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal =
+ ta.assignType(untpd.cpy.Literal(tree)(const))
+
+ override def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New =
+ ta.assignType(untpd.cpy.New(tree)(tpt), tpt)
+
+ override def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed =
+ ta.assignType(untpd.cpy.Typed(tree)(expr, tpt), tpt)
+
+ override def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg =
+ ta.assignType(untpd.cpy.NamedArg(tree)(name, arg), arg)
+
+ override def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign =
+ ta.assignType(untpd.cpy.Assign(tree)(lhs, rhs))
+
+ override def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = {
+ val tree1 = untpd.cpy.Block(tree)(stats, expr)
+ tree match {
+ case tree: Block if expr.tpe eq tree.expr.tpe => tree1.withTypeUnchecked(tree.tpe)
+ case _ => ta.assignType(tree1, stats, expr)
+ }
+ }
+
+ override def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = {
+ val tree1 = untpd.cpy.If(tree)(cond, thenp, elsep)
+ tree match {
+ case tree: If if (thenp.tpe eq tree.thenp.tpe) && (elsep.tpe eq tree.elsep.tpe) => tree1.withTypeUnchecked(tree.tpe)
+ case _ => ta.assignType(tree1, thenp, elsep)
+ }
+ }
+
+ override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure =
+ ta.assignType(untpd.cpy.Closure(tree)(env, meth, tpt), meth, tpt)
+ // Same remark as for Apply
+
+ override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = {
+ val tree1 = untpd.cpy.Match(tree)(selector, cases)
+ tree match {
+ case tree: Match if sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe)
+ case _ => ta.assignType(tree1, cases)
+ }
+ }
+
+ override def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = {
+ val tree1 = untpd.cpy.CaseDef(tree)(pat, guard, body)
+ tree match {
+ case tree: CaseDef if body.tpe eq tree.body.tpe => tree1.withTypeUnchecked(tree.tpe)
+ case _ => ta.assignType(tree1, body)
+ }
+ }
+
+ override def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return =
+ ta.assignType(untpd.cpy.Return(tree)(expr, from))
+
+ override def Try(tree: Tree)(expr: Tree, cases: List[CaseDef], finalizer: Tree)(implicit ctx: Context): Try = {
+ val tree1 = untpd.cpy.Try(tree)(expr, cases, finalizer)
+ tree match {
+ case tree: Try if (expr.tpe eq tree.expr.tpe) && sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe)
+ case _ => ta.assignType(tree1, expr, cases)
+ }
+ }
+
+ override def SeqLiteral(tree: Tree)(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = {
+ val tree1 = untpd.cpy.SeqLiteral(tree)(elems, elemtpt)
+ tree match {
+ case tree: SeqLiteral
+ if sameTypes(elems, tree.elems) && (elemtpt.tpe eq tree.elemtpt.tpe) =>
+ tree1.withTypeUnchecked(tree.tpe)
+ case _ =>
+ ta.assignType(tree1, elems, elemtpt)
+ }
+ }
+
+ override def Annotated(tree: Tree)(arg: Tree, annot: Tree)(implicit ctx: Context): Annotated = {
+ val tree1 = untpd.cpy.Annotated(tree)(arg, annot)
+ tree match {
+ case tree: Annotated if (arg.tpe eq tree.arg.tpe) && (annot eq tree.annot) => tree1.withTypeUnchecked(tree.tpe)
+ case _ => ta.assignType(tree1, arg, annot)
+ }
+ }
+
+ override def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If =
+ If(tree: Tree)(cond, thenp, elsep)
+ override def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure =
+ Closure(tree: Tree)(env, meth, tpt)
+ override def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef =
+ CaseDef(tree: Tree)(pat, guard, body)
+ override def Try(tree: Try)(expr: Tree = tree.expr, cases: List[CaseDef] = tree.cases, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try =
+ Try(tree: Tree)(expr, cases, finalizer)
+ }
+
+ implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {
+
+ def isValue(implicit ctx: Context): Boolean =
+ tree.isTerm && tree.tpe.widen.isValueType
+
+ def isValueOrPattern(implicit ctx: Context) =
+ tree.isValue || tree.isPattern
+
+ def isValueType: Boolean =
+ tree.isType && tree.tpe.isValueType
+
+ def isInstantiation: Boolean = tree match {
+ case Apply(Select(New(_), nme.CONSTRUCTOR), _) => true
+ case _ => false
+ }
+
+ def shallowFold[T](z: T)(op: (T, tpd.Tree) => T)(implicit ctx: Context) =
+ new ShallowFolder(op).apply(z, tree)
+
+ def deepFold[T](z: T)(op: (T, tpd.Tree) => T)(implicit ctx: Context) =
+ new DeepFolder(op).apply(z, tree)
+
+ def find[T](pred: (tpd.Tree) => Boolean)(implicit ctx: Context): Option[tpd.Tree] =
+ shallowFold[Option[tpd.Tree]](None)((accum, tree) => if (pred(tree)) Some(tree) else accum)
+
+ def subst(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): ThisTree =
+ new TreeTypeMap(substFrom = from, substTo = to).apply(tree)
+
+ /** Change owner from `from` to `to`. If `from` is a weak owner, also change its
+ * owner to `to`, and continue until a non-weak owner is reached.
+ */
+ def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = {
+ def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = {
+ if (from.isWeakOwner && !from.owner.isClass)
+ loop(from.owner, from :: froms, to :: tos)
+ else {
+ //println(i"change owner ${from :: froms}%, % ==> $tos of $tree")
+ new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree)
+ }
+ }
+ loop(from, Nil, to :: Nil)
+ }
+
+ /** After phase `trans`, set the owner of every definition in this tree that was formerly
+ * owner by `from` to `to`.
+ */
+ def changeOwnerAfter(from: Symbol, to: Symbol, trans: DenotTransformer)(implicit ctx: Context): ThisTree = {
+ assert(ctx.phase == trans.next)
+ val traverser = new TreeTraverser {
+ def traverse(tree: Tree)(implicit ctx: Context) = tree match {
+ case tree: DefTree =>
+ val sym = tree.symbol
+ val prevDenot = sym.denot(ctx.withPhase(trans))
+ if (prevDenot.owner == from) {
+ val d = sym.copySymDenotation(owner = to)
+ d.installAfter(trans)
+ d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d)
+ }
+ if (sym.isWeakOwner) traverseChildren(tree)
+ case _ =>
+ traverseChildren(tree)
+ }
+ }
+ traverser.traverse(tree)
+ tree
+ }
+
+ /** A select node with the given selector name and a computed type */
+ def select(name: Name)(implicit ctx: Context): Select =
+ Select(tree, name)
+
+ /** A select node with the given type */
+ def select(tp: NamedType)(implicit ctx: Context): Select =
+ untpd.Select(tree, tp.name).withType(tp)
+
+ /** A select node that selects the given symbol. Note: Need to make sure this
+ * is in fact the symbol you would get when you select with the symbol's name,
+ * otherwise a data race may occur which would be flagged by -Yno-double-bindings.
+ */
+ def select(sym: Symbol)(implicit ctx: Context): Select = {
+ val tp =
+ if (sym.isType)
+ TypeRef(tree.tpe, sym.name.asTypeName)
+ else
+ TermRef.withSigAndDenot(tree.tpe, sym.name.asTermName,
+ sym.signature, sym.denot.asSeenFrom(tree.tpe))
+ untpd.Select(tree, sym.name)
+ .withType(tp)
+ }
+
+ /** A select node with the given selector name and signature and a computed type */
+ def selectWithSig(name: Name, sig: Signature)(implicit ctx: Context): Tree =
+ untpd.SelectWithSig(tree, name, sig)
+ .withType(TermRef.withSig(tree.tpe, name.asTermName, sig))
+
+ /** A select node with selector name and signature taken from `sym`.
+ * Note: Use this method instead of select(sym) if the referenced symbol
+ * might be overridden in the type of the qualifier prefix. See note
+ * on select(sym: Symbol).
+ */
+ def selectWithSig(sym: Symbol)(implicit ctx: Context): Tree =
+ selectWithSig(sym.name, sym.signature)
+
+ /** A unary apply node with given argument: `tree(arg)` */
+ def appliedTo(arg: Tree)(implicit ctx: Context): Tree =
+ appliedToArgs(arg :: Nil)
+
+ /** An apply node with given arguments: `tree(arg, args0, ..., argsN)` */
+ def appliedTo(arg: Tree, args: Tree*)(implicit ctx: Context): Tree =
+ appliedToArgs(arg :: args.toList)
+
+ /** An apply node with given argument list `tree(args(0), ..., args(args.length - 1))` */
+ def appliedToArgs(args: List[Tree])(implicit ctx: Context): Apply =
+ Apply(tree, args)
+
+ /** The current tree applied to given argument lists:
+ * `tree (argss(0)) ... (argss(argss.length -1))`
+ */
+ def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree =
+ ((tree: Tree) /: argss)(Apply(_, _))
+
+ /** The current tree applied to (): `tree()` */
+ def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil)
+
+ /** The current tree applied to given type argument: `tree[targ]` */
+ def appliedToType(targ: Type)(implicit ctx: Context): Tree =
+ appliedToTypes(targ :: Nil)
+
+ /** The current tree applied to given type arguments: `tree[targ0, ..., targN]` */
+ def appliedToTypes(targs: List[Type])(implicit ctx: Context): Tree =
+ appliedToTypeTrees(targs map (TypeTree(_)))
+
+ /** The current tree applied to given type argument list: `tree[targs(0), ..., targs(targs.length - 1)]` */
+ def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree =
+ if (targs.isEmpty) tree else TypeApply(tree, targs)
+
+ /** Apply to `()` unless tree's widened type is parameterless */
+ def ensureApplied(implicit ctx: Context): Tree =
+ if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone
+
+ /** `tree.isInstanceOf[tp]` */
+ def isInstance(tp: Type)(implicit ctx: Context): Tree =
+ tree.select(defn.Any_isInstanceOf).appliedToType(tp)
+
+ /** tree.asInstanceOf[`tp`] */
+ def asInstance(tp: Type)(implicit ctx: Context): Tree = {
+ assert(tp.isValueType, i"bad cast: $tree.asInstanceOf[$tp]")
+ tree.select(defn.Any_asInstanceOf).appliedToType(tp)
+ }
+
+ /** `tree.asInstanceOf[tp]` (or its box/unbox/cast equivalent when after
+ * erasure and value and non-value types are mixed),
+ * unless tree's type already conforms to `tp`.
+ */
+ def ensureConforms(tp: Type)(implicit ctx: Context): Tree =
+ if (tree.tpe <:< tp) tree
+ else if (!ctx.erasedTypes) asInstance(tp)
+ else Erasure.Boxing.adaptToType(tree, tp)
+
+ /** If inititializer tree is `_', the default value of its type,
+ * otherwise the tree itself.
+ */
+ def wildcardToDefault(implicit ctx: Context) =
+ if (isWildcardArg(tree)) defaultValue(tree.tpe) else tree
+
+ /** `this && that`, for boolean trees `this`, `that` */
+ def and(that: Tree)(implicit ctx: Context): Tree =
+ tree.select(defn.Boolean_&&).appliedTo(that)
+
+ /** `this || that`, for boolean trees `this`, `that` */
+ def or(that: Tree)(implicit ctx: Context): Tree =
+ tree.select(defn.Boolean_||).appliedTo(that)
+
+ /** The translation of `tree = rhs`.
+ * This is either the tree as an assignment, to a setter call.
+ */
+ def becomes(rhs: Tree)(implicit ctx: Context): Tree =
+ if (tree.symbol is Method) {
+ val setr = tree match {
+ case Ident(_) =>
+ val setter = tree.symbol.setter
+ assert(setter.exists, tree.symbol.showLocated)
+ ref(tree.symbol.setter)
+ case Select(qual, _) => qual.select(tree.symbol.setter)
+ }
+ setr.appliedTo(rhs)
+ }
+ else Assign(tree, rhs)
+
+ // --- Higher order traversal methods -------------------------------
+
+ /** Apply `f` to each subtree of this tree */
+ def foreachSubTree(f: Tree => Unit)(implicit ctx: Context): Unit = {
+ val traverser = new TreeTraverser {
+ def traverse(tree: Tree)(implicit ctx: Context) = foldOver(f(tree), tree)
+ }
+ traverser.traverse(tree)
+ }
+
+ /** Is there a subtree of this tree that satisfies predicate `p`? */
+ def existsSubTree(p: Tree => Boolean)(implicit ctx: Context): Boolean = {
+ val acc = new TreeAccumulator[Boolean] {
+ def apply(x: Boolean, t: Tree)(implicit ctx: Context) = x || p(t) || foldOver(x, t)
+ }
+ acc(false, tree)
+ }
+
+ /** All subtrees of this tree that satisfy predicate `p`. */
+ def filterSubTrees(f: Tree => Boolean)(implicit ctx: Context): List[Tree] = {
+ val buf = new mutable.ListBuffer[Tree]
+ foreachSubTree { tree => if (f(tree)) buf += tree }
+ buf.toList
+ }
+ }
+
+ implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {
+ def tpes: List[Type] = xs map (_.tpe)
+ }
+
+ // convert a numeric with a toXXX method
+ def primitiveConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = {
+ val mname = ("to" + numericCls.name).toTermName
+ val conversion = tree.tpe member mname
+ if (conversion.symbol.exists)
+ tree.select(conversion.symbol.termRef).ensureApplied
+ else if (tree.tpe.widen isRef numericCls)
+ tree
+ else {
+ ctx.warning(i"conversion from ${tree.tpe.widen} to ${numericCls.typeRef} will always fail at runtime.")
+ Throw(New(defn.ClassCastExceptionClass.typeRef, Nil)) withPos tree.pos
+ }
+ }
+
+ /** A tree that represents the class of the erasure of type `tp`. */
+ def clsOf(tp: Type)(implicit ctx: Context): Tree = {
+ def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_)
+ defn.scalaClassName(tp) match {
+ case tpnme.Boolean => TYPE(defn.BoxedBooleanModule)
+ case tpnme.Byte => TYPE(defn.BoxedByteModule)
+ case tpnme.Short => TYPE(defn.BoxedShortModule)
+ case tpnme.Char => TYPE(defn.BoxedCharModule)
+ case tpnme.Int => TYPE(defn.BoxedIntModule)
+ case tpnme.Long => TYPE(defn.BoxedLongModule)
+ case tpnme.Float => TYPE(defn.BoxedFloatModule)
+ case tpnme.Double => TYPE(defn.BoxedDoubleModule)
+ case tpnme.Unit => TYPE(defn.BoxedUnitModule)
+ case _ =>
+ if(ctx.erasedTypes || !tp.derivesFrom(defn.ArrayClass))
+ Literal(Constant(TypeErasure.erasure(tp)))
+ else Literal(Constant(tp))
+ }
+ }
+
+ def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = {
+ val typer = ctx.typer
+ val proto = new FunProtoTyped(args, expectedType, typer)
+ val denot = receiver.tpe.member(method)
+ assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}")
+ val selected =
+ if (denot.isOverloaded) {
+ def typeParamCount(tp: Type) = tp.widen match {
+ case tp: PolyType => tp.paramBounds.length
+ case _ => 0
+ }
+ var allAlts = denot.alternatives
+ .map(_.termRef).filter(tr => typeParamCount(tr) == targs.length)
+ if (targs.isEmpty) allAlts = allAlts.filterNot(_.widen.isInstanceOf[PolyType])
+ val alternatives = ctx.typer.resolveOverloaded(allAlts, proto)
+ assert(alternatives.size == 1,
+ i"${if (alternatives.isEmpty) "no" else "multiple"} overloads available for " +
+ i"$method on ${receiver.tpe.widenDealias} with targs: $targs%, %; args: $args%, % of types ${args.tpes}%, %; expectedType: $expectedType." +
+ i" isAnnotConstructor = $isAnnotConstructor.\n" +
+ i"all alternatives: ${allAlts.map(_.symbol.showDcl).mkString(", ")}\n" +
+ i"matching alternatives: ${alternatives.map(_.symbol.showDcl).mkString(", ")}.") // this is parsed from bytecode tree. there's nothing user can do about it
+ alternatives.head
+ }
+ else denot.asSingleDenotation.termRef
+ val fun = receiver
+ .select(TermRef.withSig(receiver.tpe, selected.termSymbol.asTerm))
+ .appliedToTypes(targs)
+
+ def adaptLastArg(lastParam: Tree, expectedType: Type) = {
+ if (isAnnotConstructor && !(lastParam.tpe <:< expectedType)) {
+ val defn = ctx.definitions
+ val prefix = args.take(selected.widen.paramTypess.head.size - 1)
+ expectedType match {
+ case defn.ArrayOf(el) =>
+ lastParam.tpe match {
+ case defn.ArrayOf(el2) if el2 <:< el =>
+ // we have a JavaSeqLiteral with a more precise type
+ // we cannot construct a tree as JavaSeqLiteral infered to precise type
+ // if we add typed than it would be both type-correct and
+ // will pass Ycheck
+ prefix ::: List(tpd.Typed(lastParam, TypeTree(defn.ArrayOf(el))))
+ case _ =>
+ ???
+ }
+ case _ => args
+ }
+ } else args
+ }
+
+ val callArgs: List[Tree] = if (args.isEmpty) Nil else {
+ val expectedType = selected.widen.paramTypess.head.last
+ val lastParam = args.last
+ adaptLastArg(lastParam, expectedType)
+ }
+
+ val apply = untpd.Apply(fun, callArgs)
+ new typer.ApplyToTyped(apply, fun, selected, callArgs, expectedType).result.asInstanceOf[Tree] // needed to handle varargs
+ }
+
+ @tailrec
+ def sameTypes(trees: List[tpd.Tree], trees1: List[tpd.Tree]): Boolean = {
+ if (trees.isEmpty) trees.isEmpty
+ else if (trees1.isEmpty) trees.isEmpty
+ else (trees.head.tpe eq trees1.head.tpe) && sameTypes(trees.tail, trees1.tail)
+ }
+
+ def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = {
+ if (isIdempotentExpr(tree)) within(tree)
+ else {
+ val vdef = SyntheticValDef(ctx.freshName("ev$").toTermName, tree)
+ Block(vdef :: Nil, within(Ident(vdef.namedType)))
+ }
+ }
+
+ def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = {
+ Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args)
+ }
+
+ /** An extractor that pulls out type arguments */
+ object MaybePoly {
+ def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match {
+ case TypeApply(tree, targs) => Some(tree, targs)
+ case _ => Some(tree, Nil)
+ }
+ }
+
+ /** A traverser that passes the enclosing class or method as an argument
+ * to the traverse method.
+ */
+ abstract class EnclosingMethodTraverser extends TreeAccumulator[Symbol] {
+ def traverse(enclMeth: Symbol, tree: Tree)(implicit ctx: Context): Unit
+ def apply(enclMeth: Symbol, tree: Tree)(implicit ctx: Context) = {
+ tree match {
+ case _: DefTree if tree.symbol.exists =>
+ traverse(tree.symbol.enclosingMethod, tree)
+ case _ =>
+ traverse(enclMeth, tree)
+ }
+ enclMeth
+ }
+ }
+
+ /** A key to be used in a context property that tracks enclosing inlined calls */
+ private val InlinedCalls = new Property.Key[List[Tree]]
+
+ /** A context derived form `ctx` that records `call` as innermost enclosing
+ * call for which the inlined version is currently processed.
+ */
+ def inlineContext(call: Tree)(implicit ctx: Context): Context =
+ ctx.fresh.setProperty(InlinedCalls, call :: enclosingInlineds)
+
+ /** All enclosing calls that are currently inlined, from innermost to outermost */
+ def enclosingInlineds(implicit ctx: Context): List[Tree] =
+ ctx.property(InlinedCalls).getOrElse(Nil)
+
+ /** The source file where the symbol of the `@inline` method referred to by `call`
+ * is defined
+ */
+ def sourceFile(call: Tree)(implicit ctx: Context) = {
+ val file = call.symbol.sourceFile
+ val encoding = ctx.settings.encoding.value
+ if (file != null && file.exists) new SourceFile(file, Codec(encoding)) else NoSource
+ }
+}
+
diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala
new file mode 100644
index 000000000..6c5210287
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala
@@ -0,0 +1,562 @@
+package dotty.tools
+package dotc
+package ast
+
+import core._
+import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
+import Denotations._, SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
+import Decorators._
+import util.Property
+import language.higherKinds
+import collection.mutable.ListBuffer
+
+object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
+
+ // ----- Tree cases that exist in untyped form only ------------------
+
+ trait OpTree extends Tree {
+ def op: Name
+ override def isTerm = op.isTermName
+ override def isType = op.isTypeName
+ }
+
+ /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice
+ * @param owner The current owner at the time the tree was defined
+ */
+ abstract case class TypedSplice(tree: tpd.Tree)(val owner: Symbol) extends ProxyTree {
+ def forwardTo = tree
+ }
+
+ object TypedSplice {
+ def apply(tree: tpd.Tree)(implicit ctx: Context): TypedSplice =
+ new TypedSplice(tree)(ctx.owner) {}
+ }
+
+ /** mods object name impl */
+ case class ModuleDef(name: TermName, impl: Template)
+ extends MemberDef {
+ type ThisTree[-T >: Untyped] <: Trees.NameTree[T] with Trees.MemberDef[T] with ModuleDef
+ def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(name.toTermName, impl)
+ }
+
+ case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree) extends TermTree
+
+ case class SymbolLit(str: String) extends TermTree
+
+ /** An interpolated string
+ * @param segments a list of two element tickets consisting of string literal and argument tree,
+ * possibly with a simple string literal as last element of the list
+ */
+ case class InterpolatedString(id: TermName, segments: List[Tree]) extends TermTree
+
+ case class Function(args: List[Tree], body: Tree) extends Tree {
+ override def isTerm = body.isTerm
+ override def isType = body.isType
+ }
+ /** A function created from a wildcard expression
+ * @param placeHolderParams a list of definitions of synthetic parameters
+ * @param body the function body where wildcards are replaced by
+ * references to synthetic parameters.
+ */
+ class WildcardFunction(placeholderParams: List[ValDef], body: Tree) extends Function(placeholderParams, body)
+
+ case class InfixOp(left: Tree, op: Name, right: Tree) extends OpTree
+ case class PostfixOp(od: Tree, op: Name) extends OpTree
+ case class PrefixOp(op: Name, od: Tree) extends OpTree
+ case class Parens(t: Tree) extends ProxyTree {
+ def forwardTo = t
+ }
+ case class Tuple(trees: List[Tree]) extends Tree {
+ override def isTerm = trees.isEmpty || trees.head.isTerm
+ override def isType = !isTerm
+ }
+ case class Throw(expr: Tree) extends TermTree
+ case class WhileDo(cond: Tree, body: Tree) extends TermTree
+ case class DoWhile(body: Tree, cond: Tree) extends TermTree
+ case class ForYield(enums: List[Tree], expr: Tree) extends TermTree
+ case class ForDo(enums: List[Tree], body: Tree) extends TermTree
+ case class GenFrom(pat: Tree, expr: Tree) extends Tree
+ case class GenAlias(pat: Tree, expr: Tree) extends Tree
+ case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree]) extends TypTree
+ case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) extends DefTree
+
+ @sharable object EmptyTypeIdent extends Ident(tpnme.EMPTY) with WithoutTypeOrPos[Untyped] {
+ override def isEmpty = true
+ }
+
+ /** A block arising from a right-associative infix operation, where, e.g.
+ *
+ * a +: b
+ *
+ * is expanded to
+ *
+ * { val x = a; b.+:(x) }
+ */
+ class InfixOpBlock(leftOperand: Tree, rightOp: Tree) extends Block(leftOperand :: Nil, rightOp)
+
+ // ----- Modifiers -----------------------------------------------------
+ /** Mod is intended to record syntactic information about modifiers, it's
+ * NOT a replacement of FlagSet.
+ *
+ * For any query about semantic information, check `flags` instead.
+ */
+ sealed abstract class Mod(val flags: FlagSet) extends Positioned
+
+ object Mod {
+ case class Private() extends Mod(Flags.Private)
+
+ case class Protected() extends Mod(Flags.Protected)
+
+ case class Val() extends Mod(Flags.EmptyFlags)
+
+ case class Var() extends Mod(Flags.Mutable)
+
+ case class Implicit(flag: FlagSet = Flags.ImplicitCommon) extends Mod(flag)
+
+ case class Final() extends Mod(Flags.Final)
+
+ case class Sealed() extends Mod(Flags.Sealed)
+
+ case class Override() extends Mod(Flags.Override)
+
+ case class Abstract() extends Mod(Flags.Abstract)
+
+ case class Lazy() extends Mod(Flags.Lazy)
+
+ case class Inline() extends Mod(Flags.Inline)
+
+ case class Type() extends Mod(Flags.EmptyFlags)
+ }
+
+ /** Modifiers and annotations for definitions
+ *
+ * @param flags The set flags
+ * @param privateWithin If a private or protected has is followed by a
+ * qualifier [q], the name q, "" as a typename otherwise.
+ * @param annotations The annotations preceding the modifiers
+ */
+ case class Modifiers (
+ flags: FlagSet = EmptyFlags,
+ privateWithin: TypeName = tpnme.EMPTY,
+ annotations: List[Tree] = Nil,
+ mods: List[Mod] = Nil) extends Positioned with Cloneable {
+
+ def is(fs: FlagSet): Boolean = flags is fs
+ def is(fc: FlagConjunction): Boolean = flags is fc
+
+ def | (fs: FlagSet): Modifiers = withFlags(flags | fs)
+ def & (fs: FlagSet): Modifiers = withFlags(flags & fs)
+ def &~(fs: FlagSet): Modifiers = withFlags(flags &~ fs)
+
+ def toTypeFlags: Modifiers = withFlags(flags.toTypeFlags)
+ def toTermFlags: Modifiers = withFlags(flags.toTermFlags)
+
+ def withFlags(flags: FlagSet) =
+ if (this.flags == flags) this
+ else copy(flags = flags)
+
+ def withAddedMod(mod: Mod): Modifiers =
+ if (mods.exists(_ eq mod)) this
+ else withMods(mods :+ mod)
+
+ def withMods(ms: List[Mod]): Modifiers =
+ if (mods eq ms) this
+ else copy(mods = ms)
+
+ def withAddedAnnotation(annot: Tree): Modifiers =
+ if (annotations.exists(_ eq annot)) this
+ else withAnnotations(annotations :+ annot)
+
+ def withAnnotations(annots: List[Tree]): Modifiers =
+ if (annots eq annotations) this
+ else copy(annotations = annots)
+
+ def withPrivateWithin(pw: TypeName) =
+ if (pw.isEmpty) this
+ else copy(privateWithin = pw)
+
+ def hasFlags = flags != EmptyFlags
+ def hasAnnotations = annotations.nonEmpty
+ def hasPrivateWithin = privateWithin != tpnme.EMPTY
+ }
+
+ @sharable val EmptyModifiers: Modifiers = new Modifiers()
+
+ // ----- TypeTrees that refer to other tree's symbols -------------------
+
+ /** A type tree that gets its type from some other tree's symbol. Enters the
+ * type tree in the References attachment of the `from` tree as a side effect.
+ */
+ abstract class DerivedTypeTree extends TypeTree {
+
+ private var myWatched: Tree = EmptyTree
+
+ /** The watched tree; used only for printing */
+ def watched: Tree = myWatched
+
+ /** Install the derived type tree as a dependency on `original` */
+ def watching(original: DefTree): this.type = {
+ myWatched = original
+ val existing = original.attachmentOrElse(References, Nil)
+ original.putAttachment(References, this :: existing)
+ this
+ }
+
+ /** A hook to ensure that all necessary symbols are completed so that
+ * OriginalSymbol attachments are propagated to this tree
+ */
+ def ensureCompletions(implicit ctx: Context): Unit = ()
+
+ /** The method that computes the type of this tree */
+ def derivedType(originalSym: Symbol)(implicit ctx: Context): Type
+ }
+
+ /** Property key containing TypeTrees whose type is computed
+ * from the symbol in this type. These type trees have marker trees
+ * TypeRefOfSym or InfoOfSym as their originals.
+ */
+ val References = new Property.Key[List[Tree]]
+
+ /** Property key for TypeTrees marked with TypeRefOfSym or InfoOfSym
+ * which contains the symbol of the original tree from which this
+ * TypeTree is derived.
+ */
+ val OriginalSymbol = new Property.Key[Symbol]
+
+ // ------ Creation methods for untyped only -----------------
+
+ def Ident(name: Name): Ident = new Ident(name)
+ def BackquotedIdent(name: Name): BackquotedIdent = new BackquotedIdent(name)
+ def Select(qualifier: Tree, name: Name): Select = new Select(qualifier, name)
+ def SelectWithSig(qualifier: Tree, name: Name, sig: Signature): Select = new SelectWithSig(qualifier, name, sig)
+ def This(qual: Ident): This = new This(qual)
+ def Super(qual: Tree, mix: Ident): Super = new Super(qual, mix)
+ def Apply(fun: Tree, args: List[Tree]): Apply = new Apply(fun, args)
+ def TypeApply(fun: Tree, args: List[Tree]): TypeApply = new TypeApply(fun, args)
+ def Literal(const: Constant): Literal = new Literal(const)
+ def New(tpt: Tree): New = new New(tpt)
+ def Typed(expr: Tree, tpt: Tree): Typed = new Typed(expr, tpt)
+ def NamedArg(name: Name, arg: Tree): NamedArg = new NamedArg(name, arg)
+ def Assign(lhs: Tree, rhs: Tree): Assign = new Assign(lhs, rhs)
+ def Block(stats: List[Tree], expr: Tree): Block = new Block(stats, expr)
+ def If(cond: Tree, thenp: Tree, elsep: Tree): If = new If(cond, thenp, elsep)
+ def Closure(env: List[Tree], meth: Tree, tpt: Tree): Closure = new Closure(env, meth, tpt)
+ def Match(selector: Tree, cases: List[CaseDef]): Match = new Match(selector, cases)
+ def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body)
+ def Return(expr: Tree, from: Tree): Return = new Return(expr, from)
+ def Try(expr: Tree, cases: List[CaseDef], finalizer: Tree): Try = new Try(expr, cases, finalizer)
+ def SeqLiteral(elems: List[Tree], elemtpt: Tree): SeqLiteral = new SeqLiteral(elems, elemtpt)
+ def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt)
+ def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree): Inlined = new Inlined(call, bindings, expansion)
+ def TypeTree() = new TypeTree()
+ def SingletonTypeTree(ref: Tree): SingletonTypeTree = new SingletonTypeTree(ref)
+ def AndTypeTree(left: Tree, right: Tree): AndTypeTree = new AndTypeTree(left, right)
+ def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right)
+ def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements)
+ def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args)
+ def PolyTypeTree(tparams: List[TypeDef], body: Tree): PolyTypeTree = new PolyTypeTree(tparams, body)
+ def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result)
+ def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi)
+ def Bind(name: Name, body: Tree): Bind = new Bind(name, body)
+ def Alternative(trees: List[Tree]): Alternative = new Alternative(trees)
+ def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = new UnApply(fun, implicits, patterns)
+ def ValDef(name: TermName, tpt: Tree, rhs: LazyTree): ValDef = new ValDef(name, tpt, rhs)
+ def DefDef(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs)
+ def TypeDef(name: TypeName, rhs: Tree): TypeDef = new TypeDef(name, rhs)
+ def Template(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList): Template = new Template(constr, parents, self, body)
+ def Import(expr: Tree, selectors: List[untpd.Tree]): Import = new Import(expr, selectors)
+ def PackageDef(pid: RefTree, stats: List[Tree]): PackageDef = new PackageDef(pid, stats)
+ def Annotated(arg: Tree, annot: Tree): Annotated = new Annotated(arg, annot)
+
+ // ------ Additional creation methods for untyped only -----------------
+
+ // def TypeTree(tpe: Type): TypeTree = TypeTree().withType(tpe) todo: move to untpd/tpd
+
+ /** new pre.C[Ts](args1)...(args_n)
+ * ==>
+ * (new pre.C).<init>[Ts](args1)...(args_n)
+ */
+ def New(tpt: Tree, argss: List[List[Tree]])(implicit ctx: Context): Tree = {
+ val (tycon, targs) = tpt match {
+ case AppliedTypeTree(tycon, targs) =>
+ (tycon, targs)
+ case TypedSplice(AppliedTypeTree(tycon, targs)) =>
+ (TypedSplice(tycon), targs map (TypedSplice(_)))
+ case TypedSplice(tpt1: Tree) =>
+ val argTypes = tpt1.tpe.argTypesLo
+ val tycon = tpt1.tpe.withoutArgs(argTypes)
+ def wrap(tpe: Type) = TypeTree(tpe) withPos tpt.pos
+ (wrap(tycon), argTypes map wrap)
+ case _ =>
+ (tpt, Nil)
+ }
+ var prefix: Tree = Select(New(tycon), nme.CONSTRUCTOR)
+ if (targs.nonEmpty) prefix = TypeApply(prefix, targs)
+ ensureApplied((prefix /: argss)(Apply(_, _)))
+ }
+
+ def Block(stat: Tree, expr: Tree): Block =
+ Block(stat :: Nil, expr)
+
+ def Apply(fn: Tree, arg: Tree): Apply =
+ Apply(fn, arg :: Nil)
+
+ def ensureApplied(tpt: Tree) = tpt match {
+ case _: Apply => tpt
+ case _ => Apply(tpt, Nil)
+ }
+
+ def AppliedTypeTree(tpt: Tree, arg: Tree): AppliedTypeTree =
+ AppliedTypeTree(tpt, arg :: Nil)
+
+ def TypeTree(tpe: Type)(implicit ctx: Context): TypedSplice = TypedSplice(TypeTree().withTypeUnchecked(tpe))
+
+ def unitLiteral = Literal(Constant(()))
+
+ def ref(tp: NamedType)(implicit ctx: Context): Tree =
+ TypedSplice(tpd.ref(tp))
+
+ def rootDot(name: Name) = Select(Ident(nme.ROOTPKG), name)
+ def scalaDot(name: Name) = Select(rootDot(nme.scala_), name)
+ def scalaUnit = scalaDot(tpnme.Unit)
+ def scalaAny = scalaDot(tpnme.Any)
+
+ def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef =
+ DefDef(nme.CONSTRUCTOR, tparams, vparamss, TypeTree(), rhs)
+
+ def emptyConstructor(implicit ctx: Context): DefDef =
+ makeConstructor(Nil, Nil)
+
+ def makeSelfDef(name: TermName, tpt: Tree)(implicit ctx: Context) =
+ ValDef(name, tpt, EmptyTree).withFlags(PrivateLocal)
+
+ def makeTupleOrParens(ts: List[Tree])(implicit ctx: Context) = ts match {
+ case t :: Nil => Parens(t)
+ case _ => Tuple(ts)
+ }
+
+ def makeTuple(ts: List[Tree])(implicit ctx: Context) = ts match {
+ case t :: Nil => t
+ case _ => Tuple(ts)
+ }
+
+ def makeParameter(pname: TermName, tpe: Tree, mods: Modifiers = EmptyModifiers)(implicit ctx: Context): ValDef =
+ ValDef(pname, tpe, EmptyTree).withMods(mods | Param)
+
+ def makeSyntheticParameter(n: Int = 1, tpt: Tree = TypeTree())(implicit ctx: Context): ValDef =
+ ValDef(nme.syntheticParamName(n), tpt, EmptyTree).withFlags(SyntheticTermParam)
+
+ def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context) =
+ if (tparams.isEmpty) tpt else PolyTypeTree(tparams, tpt)
+
+ /** A reference to given definition. If definition is a repeated
+ * parameter, the reference will be a repeated argument.
+ */
+ def refOfDef(tree: MemberDef)(implicit ctx: Context) = tree match {
+ case ValDef(_, PostfixOp(_, nme.raw.STAR), _) => repeated(Ident(tree.name))
+ case _ => Ident(tree.name)
+ }
+
+ /** A repeated argument such as `arg: _*` */
+ def repeated(arg: Tree)(implicit ctx: Context) = Typed(arg, Ident(tpnme.WILDCARD_STAR))
+
+// ----- Accessing modifiers ----------------------------------------------------
+
+ abstract class ModsDecorator { def mods: Modifiers }
+
+ implicit class modsDeco(val mdef: MemberDef)(implicit ctx: Context) {
+ def mods = mdef.rawMods
+ }
+
+// --------- Copier/Transformer/Accumulator classes for untyped trees -----
+
+ override val cpy: UntypedTreeCopier = new UntypedTreeCopier
+
+ class UntypedTreeCopier extends TreeCopier {
+
+ def postProcess(tree: Tree, copied: Tree): copied.ThisTree[Untyped] =
+ copied.asInstanceOf[copied.ThisTree[Untyped]]
+
+ def postProcess(tree: Tree, copied: MemberDef): copied.ThisTree[Untyped] = {
+ tree match {
+ case tree: MemberDef => copied.withMods(tree.rawMods)
+ case _ => copied
+ }
+ }.asInstanceOf[copied.ThisTree[Untyped]]
+
+ def ModuleDef(tree: Tree)(name: TermName, impl: Template) = tree match {
+ case tree: ModuleDef if (name eq tree.name) && (impl eq tree.impl) => tree
+ case _ => untpd.ModuleDef(name, impl).withPos(tree.pos)
+ }
+ def ParsedTry(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree) = tree match {
+ case tree: ParsedTry
+ if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree
+ case _ => untpd.ParsedTry(expr, handler, finalizer).withPos(tree.pos)
+ }
+ def SymbolLit(tree: Tree)(str: String) = tree match {
+ case tree: SymbolLit if str == tree.str => tree
+ case _ => untpd.SymbolLit(str).withPos(tree.pos)
+ }
+ def InterpolatedString(tree: Tree)(id: TermName, segments: List[Tree]) = tree match {
+ case tree: InterpolatedString if (id eq tree.id) && (segments eq tree.segments) => tree
+ case _ => untpd.InterpolatedString(id, segments).withPos(tree.pos)
+ }
+ def Function(tree: Tree)(args: List[Tree], body: Tree) = tree match {
+ case tree: Function if (args eq tree.args) && (body eq tree.body) => tree
+ case _ => untpd.Function(args, body).withPos(tree.pos)
+ }
+ def InfixOp(tree: Tree)(left: Tree, op: Name, right: Tree) = tree match {
+ case tree: InfixOp if (left eq tree.left) && (op eq tree.op) && (right eq tree.right) => tree
+ case _ => untpd.InfixOp(left, op, right).withPos(tree.pos)
+ }
+ def PostfixOp(tree: Tree)(od: Tree, op: Name) = tree match {
+ case tree: PostfixOp if (od eq tree.od) && (op eq tree.op) => tree
+ case _ => untpd.PostfixOp(od, op).withPos(tree.pos)
+ }
+ def PrefixOp(tree: Tree)(op: Name, od: Tree) = tree match {
+ case tree: PrefixOp if (op eq tree.op) && (od eq tree.od) => tree
+ case _ => untpd.PrefixOp(op, od).withPos(tree.pos)
+ }
+ def Parens(tree: Tree)(t: Tree) = tree match {
+ case tree: Parens if t eq tree.t => tree
+ case _ => untpd.Parens(t).withPos(tree.pos)
+ }
+ def Tuple(tree: Tree)(trees: List[Tree]) = tree match {
+ case tree: Tuple if trees eq tree.trees => tree
+ case _ => untpd.Tuple(trees).withPos(tree.pos)
+ }
+ def Throw(tree: Tree)(expr: Tree) = tree match {
+ case tree: Throw if expr eq tree.expr => tree
+ case _ => untpd.Throw(expr).withPos(tree.pos)
+ }
+ def WhileDo(tree: Tree)(cond: Tree, body: Tree) = tree match {
+ case tree: WhileDo if (cond eq tree.cond) && (body eq tree.body) => tree
+ case _ => untpd.WhileDo(cond, body).withPos(tree.pos)
+ }
+ def DoWhile(tree: Tree)(body: Tree, cond: Tree) = tree match {
+ case tree: DoWhile if (body eq tree.body) && (cond eq tree.cond) => tree
+ case _ => untpd.DoWhile(body, cond).withPos(tree.pos)
+ }
+ def ForYield(tree: Tree)(enums: List[Tree], expr: Tree) = tree match {
+ case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree
+ case _ => untpd.ForYield(enums, expr).withPos(tree.pos)
+ }
+ def ForDo(tree: Tree)(enums: List[Tree], body: Tree) = tree match {
+ case tree: ForDo if (enums eq tree.enums) && (body eq tree.body) => tree
+ case _ => untpd.ForDo(enums, body).withPos(tree.pos)
+ }
+ def GenFrom(tree: Tree)(pat: Tree, expr: Tree) = tree match {
+ case tree: GenFrom if (pat eq tree.pat) && (expr eq tree.expr) => tree
+ case _ => untpd.GenFrom(pat, expr).withPos(tree.pos)
+ }
+ def GenAlias(tree: Tree)(pat: Tree, expr: Tree) = tree match {
+ case tree: GenAlias if (pat eq tree.pat) && (expr eq tree.expr) => tree
+ case _ => untpd.GenAlias(pat, expr).withPos(tree.pos)
+ }
+ def ContextBounds(tree: Tree)(bounds: TypeBoundsTree, cxBounds: List[Tree]) = tree match {
+ case tree: ContextBounds if (bounds eq tree.bounds) && (cxBounds eq tree.cxBounds) => tree
+ case _ => untpd.ContextBounds(bounds, cxBounds).withPos(tree.pos)
+ }
+ def PatDef(tree: Tree)(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) = tree match {
+ case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree
+ case _ => untpd.PatDef(mods, pats, tpt, rhs).withPos(tree.pos)
+ }
+ }
+
+ abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) {
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case ModuleDef(name, impl) =>
+ cpy.ModuleDef(tree)(name, transformSub(impl))
+ case ParsedTry(expr, handler, finalizer) =>
+ cpy.ParsedTry(tree)(transform(expr), transform(handler), transform(finalizer))
+ case SymbolLit(str) =>
+ cpy.SymbolLit(tree)(str)
+ case InterpolatedString(id, segments) =>
+ cpy.InterpolatedString(tree)(id, transform(segments))
+ case Function(args, body) =>
+ cpy.Function(tree)(transform(args), transform(body))
+ case InfixOp(left, op, right) =>
+ cpy.InfixOp(tree)(transform(left), op, transform(right))
+ case PostfixOp(od, op) =>
+ cpy.PostfixOp(tree)(transform(od), op)
+ case PrefixOp(op, od) =>
+ cpy.PrefixOp(tree)(op, transform(od))
+ case Parens(t) =>
+ cpy.Parens(tree)(transform(t))
+ case Tuple(trees) =>
+ cpy.Tuple(tree)(transform(trees))
+ case Throw(expr) =>
+ cpy.Throw(tree)(transform(expr))
+ case WhileDo(cond, body) =>
+ cpy.WhileDo(tree)(transform(cond), transform(body))
+ case DoWhile(body, cond) =>
+ cpy.DoWhile(tree)(transform(body), transform(cond))
+ case ForYield(enums, expr) =>
+ cpy.ForYield(tree)(transform(enums), transform(expr))
+ case ForDo(enums, body) =>
+ cpy.ForDo(tree)(transform(enums), transform(body))
+ case GenFrom(pat, expr) =>
+ cpy.GenFrom(tree)(transform(pat), transform(expr))
+ case GenAlias(pat, expr) =>
+ cpy.GenAlias(tree)(transform(pat), transform(expr))
+ case ContextBounds(bounds, cxBounds) =>
+ cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds))
+ case PatDef(mods, pats, tpt, rhs) =>
+ cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
+ case _ =>
+ super.transform(tree)
+ }
+ }
+
+ abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] {
+ override def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
+ case ModuleDef(name, impl) =>
+ this(x, impl)
+ case ParsedTry(expr, handler, finalizer) =>
+ this(this(this(x, expr), handler), finalizer)
+ case SymbolLit(str) =>
+ x
+ case InterpolatedString(id, segments) =>
+ this(x, segments)
+ case Function(args, body) =>
+ this(this(x, args), body)
+ case InfixOp(left, op, right) =>
+ this(this(x, left), right)
+ case PostfixOp(od, op) =>
+ this(x, od)
+ case PrefixOp(op, od) =>
+ this(x, od)
+ case Parens(t) =>
+ this(x, t)
+ case Tuple(trees) =>
+ this(x, trees)
+ case Throw(expr) =>
+ this(x, expr)
+ case WhileDo(cond, body) =>
+ this(this(x, cond), body)
+ case DoWhile(body, cond) =>
+ this(this(x, body), cond)
+ case ForYield(enums, expr) =>
+ this(this(x, enums), expr)
+ case ForDo(enums, body) =>
+ this(this(x, enums), body)
+ case GenFrom(pat, expr) =>
+ this(this(x, pat), expr)
+ case GenAlias(pat, expr) =>
+ this(this(x, pat), expr)
+ case ContextBounds(bounds, cxBounds) =>
+ this(this(x, bounds), cxBounds)
+ case PatDef(mods, pats, tpt, rhs) =>
+ this(this(this(x, pats), tpt), rhs)
+ case TypedSplice(tree) =>
+ this(x, tree)
+ case _ =>
+ super.foldOver(x, tree)
+ }
+ }
+
+ /** Fold `f` over all tree nodes, in depth-first, prefix order */
+ class UntypedDeepFolder[X](f: (X, Tree) => X) extends UntypedTreeAccumulator[X] {
+ def apply(x: X, tree: Tree)(implicit ctx: Context): X = foldOver(f(x, tree), tree)
+ }
+}