diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-11-02 11:08:28 +0100 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-11-22 01:35:07 +0100 |
commit | 8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch) | |
tree | a8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/ast | |
parent | 6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff) | |
download | dotty-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.disabled | 258 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/Desugar.scala | 1089 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/NavigateAST.scala | 82 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/PluggableTransformers.scala | 105 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/Positioned.scala | 213 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 733 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala | 187 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/Trees.scala | 1295 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/tpd.scala | 952 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/ast/untpd.scala | 562 |
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) + } +} |