diff options
author | Martin Odersky <odersky@gmail.com> | 2013-04-19 14:41:11 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-04-19 14:41:11 +0200 |
commit | 6b58c275293b2c2b0bf3390eaee94ec31bcbdab9 (patch) | |
tree | 74c264e1483b9a346821c53ad09df10c69e5f8bd /src/dotty/tools/dotc | |
parent | 344450ffcde551af8406fa1b873a6cb9dd430eb0 (diff) | |
download | dotty-6b58c275293b2c2b0bf3390eaee94ec31bcbdab9.tar.gz dotty-6b58c275293b2c2b0bf3390eaee94ec31bcbdab9.tar.bz2 dotty-6b58c275293b2c2b0bf3390eaee94ec31bcbdab9.zip |
Added TreeBuilder and TreeInfo classes.
Also changed Untyped from Nothing to Null, because it avoids type inference problems.
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r-- | src/dotty/tools/dotc/core/Printers.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TreeInfo.scala | 479 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Trees.scala | 338 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 7 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/UntypedTrees.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/UnPickler.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/parsing/TreeBuilder.scala | 548 | ||||
-rw-r--r-- | src/dotty/tools/dotc/reporting/Reporter.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/util/Positions.scala | 2 |
9 files changed, 1239 insertions, 155 deletions
diff --git a/src/dotty/tools/dotc/core/Printers.scala b/src/dotty/tools/dotc/core/Printers.scala index 6255a79da..2828ade46 100644 --- a/src/dotty/tools/dotc/core/Printers.scala +++ b/src/dotty/tools/dotc/core/Printers.scala @@ -85,7 +85,7 @@ object Printers { def toText(sc: Scope): Text /** Textual representation of tree */ - def toText[T](tree: Tree[T]): Text + def toText[T >: Untyped](tree: Tree[T]): Text } class PlainPrinter(_ctx: Context) extends Printer { @@ -403,7 +403,7 @@ object Printers { def toText(sc: Scope): Text = ("Scope{" ~ dclsText(sc.toList) ~ "}").close - def toText[T](tree: Tree[T]): Text = { + def toText[T >: Untyped](tree: Tree[T]): Text = { tree match { case node: Product => def toTextElem(elem: Any): Text = elem match { @@ -431,10 +431,10 @@ object Printers { override def nameString(name: Name): String = name.toString - override protected def simpleNameString(sym: Symbol) = { + override protected def simpleNameString(sym: Symbol): String = { var name = sym.originalName if (sym is ModuleClass) name = name.stripModuleClassSuffix - name.decode + name.decode.toString } override def toTextPrefix(tp: Type): Text = controlled { diff --git a/src/dotty/tools/dotc/core/TreeInfo.scala b/src/dotty/tools/dotc/core/TreeInfo.scala new file mode 100644 index 000000000..cb621164c --- /dev/null +++ b/src/dotty/tools/dotc/core/TreeInfo.scala @@ -0,0 +1,479 @@ +package dotty.tools +package dotc +package core + +import Flags._, Trees._, UntypedTrees._, TypedTrees._, Types._, Contexts._ +import Names._, StdNames._, NameOps._, Decorators._, Symbols._ +import util.HashSet + +/** This class ... + * + * @author Martin Odersky + * @version 1.0 + */ +abstract class TreeInfo { + + def isDeclarationOrTypeDef(tree: Tree[_ >: Untyped]): Boolean = tree match { + case DefDef(_, _, _, _, _, EmptyTree()) + | ValDef(_, _, _, EmptyTree()) + | TypeDef(_, _, _) => true + case _ => false + } + + /** Is tree legal as a member definition of an interface? + */ + def isInterfaceMember(tree: Tree[_ >: Untyped]): Boolean = tree match { + case EmptyTree() => true + case Import(_, _) => true + case TypeDef(_, _, _) => true + case DefDef(mods, _, _, _, _, __) => mods.flags is Deferred + case ValDef(mods, _, _, _) => mods is Deferred + case _ => false + } + + /** Is tree a definition that has no side effects when + * evaluated as part of a block after the first time? + */ + def isIdempotentDef(tree: Tree[Type])(implicit ctx: Context): Boolean = tree match { + case EmptyTree() + | ClassDef(_, _, _, _) + | TypeDef(_, _, _) + | Import(_, _) + | DefDef(_, _, _, _, _, _) => + true + case ValDef(mods, _, _, rhs) => + !(mods is Mutable) && isIdempotentExpr(rhs) + case _ => + false + } + + /** Is tree an expression which can be inlined without affecting program semantics? + * + * Note that this is not called "isExprPure" since purity (lack of side-effects) + * is not the litmus test. References to modules and lazy vals are 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 safe to inline + * because the expression result from evaluating them is always the same. + */ + def isIdempotentExpr(tree: Tree[Type])(implicit ctx: Context): Boolean = tree match { + case EmptyTree() + | This(_) + | Super(_, _) + | Literal(_) => + true + case Ident(_) => + tree.symbol is Stable + case Select(qual, _) => + tree.symbol.isStable && isIdempotentExpr(qual) + case TypeApply(fn, _) => + isIdempotentExpr(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, Nil) => + // Note: After uncurry, field accesses are represented as Apply(getter, Nil), + // so an Apply can also be pure. + // However, before typing, applications of nullary functional values are also + // Apply(function, Nil) trees. To prevent them from being treated as pure, + // we check that the callee is a method. + // The callee might also be a Block, which has a null symbol, so we guard against that (SI-7185) + fn.symbol != null && (fn.symbol is (Method, butNot = Lazy)) && isIdempotentExpr(fn) + case Typed(expr, _) => + isIdempotentExpr(expr) + case Block(stats, expr) => + (stats forall isIdempotentDef) && isIdempotentExpr(expr) + case _ => + false + } + + class MatchingArgs[T >: Untyped](params: List[Symbol], args: List[Tree[T]])(implicit ctx: Context) { + def foreach(f: (Symbol, Tree[T]) => Unit): Boolean = { + def recur(params: List[Symbol], args: List[Tree[T]]): Boolean = params match { + case Nil => args.isEmpty + case param :: params1 => + if (defn.RepeatedParamClasses contains param.info.typeSymbol) { + 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[T])] = map((_, _)) + def map[R](f: (Symbol, Tree[T]) => 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[T >: Untyped](tree: Tree[T]): Tree[T] = tree match { + case Apply(fn, _) => methPart(fn) + case TypeApply(fn, _) => methPart(fn) + case AppliedTypeTree(fn, _) => methPart(fn) + case Block(stats, expr) if stats forall (_.isInstanceOf[ValDef[_]]) => methPart(expr) + case _ => tree + } + + /** 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[Type])(implicit ctx: Context) = { + def sym = tree.symbol + def isVar = sym is Mutable + def isGetter = + mayBeVarGetter(sym) && sym.owner.info.member(sym.name.asTermName.getterToSetter).exists + + 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 self constructor call this(...)? I.e. a call to a constructor of the + * same object? + */ + def isSelfConstrCall(tree: Tree[_ >: Untyped]): 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[_ >: Untyped]): Boolean = methPart(tree) match { + case Select(Super(_, _), nme.CONSTRUCTOR) => true + case _ => false + } + + def isSelfOrSuperConstrCall(tree: Tree[_ >: Untyped]): Boolean = methPart(tree) match { + case Ident(nme.CONSTRUCTOR) + | Select(This(_), nme.CONSTRUCTOR) + | Select(Super(_, _), nme.CONSTRUCTOR) => true + case _ => false + } + + /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ + def stripCast(tree: Tree[Type])(implicit ctx: Context): Tree[Type] = { + def isCast(sel: Tree[Type]) = defn.asInstanceOfMethods contains sel.symbol + 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 + } + } + + /** Is tree a variable pattern? */ + def isVarPattern[T >: Untyped](pat: Tree[T]): Boolean = pat match { + case x: BackquotedIdent[_] => false + case x: Ident[_] => x.name.isVariableName + case _ => false + } + + /** The first constructor definition in `stats` */ + def firstConstructor[T >: Untyped](stats: List[Tree[T]]): Tree[T] = 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[T >: Untyped](stats: List[Tree[T]]): List[Tree[T]] = firstConstructor(stats) match { + case DefDef(_, _, _, args :: _, _, _) => args + case _ => Nil + } + + /** The value definitions marked PRESUPER in this statement sequence */ + def preSuperFields[T >: Untyped](stats: List[Tree[T]]): List[ValDef[T]] = + (stats filter isEarlyValDef).asInstanceOf[List[ValDef[T]]] + + def isEarlyDef(tree: Tree[_ >: Untyped]) = isEarlyValDef(tree) || isEarlyTypeDef(tree) + + def isEarlyValDef(tree: Tree[_ >: Untyped]) = tree match { + case ValDef(mods, _, _, _) => mods is Scala2PreSuper + case _ => false + } + + def isEarlyTypeDef(tree: Tree[_ >: Untyped]) = tree match { + case TypeDef(mods, _, _) => mods is Scala2PreSuper + case _ => false + } + + /** Is tpt a vararg type of the form T* ? */ + def isRepeatedParamType(tpt: Tree[_ >: Untyped])(implicit ctx: Context) = tpt match { + case tpt: TypeTree[_] => defn.RepeatedParamClasses contains tpt.typeOpt.typeSymbol + case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true + case AppliedTypeTree(Select(_, tpnme.JAVA_REPEATED_PARAM_CLASS), _) => true + case _ => false + } + + /** Is tpt a by-name parameter type of the form => T? */ + def isByNameParamType(tpt: Tree[_ >: Untyped])(implicit ctx: Context) = tpt match { + case tpt: TypeTree[_] => tpt.typeOpt.typeSymbol == defn.ByNameParamClass + case AppliedTypeTree(Select(_, tpnme.BYNAME_PARAM_CLASS), _) => true + case _ => false + } + + /** Is name a left-associative operator? */ + def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.last != ':') + + /** Is tree a `this` node which belongs to `enclClass`? */ + def isSelf(tree: Tree[_ >: Untyped], enclClass: Symbol)(implicit ctx: Context): Boolean = tree match { + case This(_) => tree.symbol == enclClass + case _ => false + } + + /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) + def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation defn.SwitchClass + */ + + /** can this type be a type pattern? */ + def mayBeTypePat(tree: Tree[Untyped]): Boolean = tree match { + case AndTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) + case OrTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) + case RefineTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind[_]]) + case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind[_]]) + case SelectFromTypeTree(tpt, _) => mayBeTypePat(tpt) + case Annotated(_, tpt) => mayBeTypePat(tpt) + case _ => false + } + + /** Is this argument node of the form <expr> : _* ? + */ + def isWildcardStarArg(tree: Tree[_ >: Untyped]): Boolean = tree match { + case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true + 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[_ >: Untyped]]) = + trees.nonEmpty && isWildcardStarArg(trees.last) + + /** Is the argument a wildcard argument of the form `_` or `x @ _`? + */ + def isWildcardArg(tree: Tree[_ >: Untyped]): Boolean = unbind(tree) match { + case Ident(nme.WILDCARD) => true + case _ => false + } + + /** Is the argument a wildcard star type of the form `_*`? + */ + def isWildcardStarType(tree: Tree[_ >: Untyped]): Boolean = tree match { + case Ident(tpnme.WILDCARD_STAR) => true + case _ => false + } + + /** Is this pattern node a catch-all (wildcard or variable) pattern? */ + def isDefaultCase(cdef: CaseDef[_ >: Untyped]) = 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[_ >: Untyped]) = cdef match { + case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree(), _) => true + case _ => false + } + + /** Does this CaseDef catch Throwable? */ + def catchesThrowable(cdef: CaseDef[_ >: Untyped])(implicit ctx: Context) = + catchesAllOf(cdef, defn.ThrowableClass.typeConstructor) + + /** Does this CaseDef catch everything of a certain Type? */ + def catchesAllOf(cdef: CaseDef[_ >: Untyped], 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 pattern node a catch-all or type-test pattern? */ + def isCatchCase(cdef: CaseDef[Type])(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 + } + + /** Is this case guarded? */ + def isGuardedCase(cdef: CaseDef[_ >: Untyped]) = cdef.guard != EmptyTree() + + /** Is this pattern node a sequence-valued pattern? */ + def isSequenceValued(tree: Tree[_ >: Untyped]): Boolean = unbind(tree) match { + case Alternative(ts) => ts exists isSequenceValued + case SeqLiteral(_, _) => true + case _ => false + } + + /** The underlying pattern ignoring any bindings */ + def unbind[T >: Untyped](x: Tree[T]): Tree[T] = x match { + case Bind(_, y) => unbind(y) + case y => y + } + + + /** 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 + } + } + + def isApplyDynamicName(name: Name) = (name == nme.updateDynamic) || (name == nme.selectDynamic) || (name == nme.applyDynamic) || (name == nme.applyDynamicNamed) + + class DynamicApplicationExtractor(nameTest: Name => Boolean) { + def unapply(tree: Tree) = tree match { + case Apply(TypeApply(Select(qual, oper), _), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Select(qual, oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Ident(oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((EmptyTree(), name)) + case _ => None + } + } + object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic) + object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName) + object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed) + + 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 + })*/ +} +object treeInfo extends TreeInfo
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Trees.scala b/src/dotty/tools/dotc/core/Trees.scala index c485eddc1..664b7f41b 100644 --- a/src/dotty/tools/dotc/core/Trees.scala +++ b/src/dotty/tools/dotc/core/Trees.scala @@ -5,17 +5,40 @@ import Types._, Names._, Flags._, util.Positions._, Contexts._, Constants._, Sym import Denotations._, StdNames._ import annotation.tailrec import language.higherKinds +import collection.mutable object Trees { - case class Modifiers[T]( - flags: FlagSet, + // 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 soemthing like Select(pre, name), where pre is + // of type Tree[Nothing]; type inference will treat the Nothing as an uninstantited + // 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 + type TypedTree = Tree[Type] + type UntypedTree = Tree[Untyped] + + case class Modifiers[T >: Untyped]( + flags: FlagSet = EmptyFlags, privateWithin: TypeName = tpnme.EMPTY, - annotations: List[Tree[T]] = Nil) + annotations: List[Tree[T]] = Nil) { + + def | (fs: FlagSet) = copy(flags = flags | fs) + def & (fs: FlagSet) = copy(flags = flags & fs) + def &~(fs: FlagSet) = copy(flags = flags &~ fs) + def is(fs: FlagSet) = flags is fs + def is(fc: FlagConjunction) = flags is fc + } + + private val modCache = mutable.Map[FlagSet, Modifiers[Untyped]]() + + def apply[T >: Untyped](flags: FlagSet = EmptyFlags): Modifiers[T] = + modCache.getOrElseUpdate(flags, Modifiers(flags, tpnme.EMPTY, Nil)).asInstanceOf[Modifiers[T]] /** Trees take a parameter indicating what the type of their `tpe` field - * is. Two choices: `Type` or `Nothing`. - * Untyped trees have type `Tree[Nothing]`. + * is. Two choices: `Type` or `Untyped`. + * Untyped trees have type `Tree[Untyped]`. * * Tree typing uses a copy-on-write implementation: * @@ -29,7 +52,7 @@ object Trees { * - Type checking an untyped tree should remove all embedded `TypedSplice` * nodes. */ - abstract class Tree[T] extends DotClass with Showable with Cloneable { + abstract class Tree[T >: Untyped] extends DotClass with Showable with Cloneable { /** The tree's position. Except * for SharedTree nodes, it is always ensured that a tree's position @@ -38,7 +61,7 @@ object Trees { def pos: Position /** The type constructor at the root of the tree */ - type ThisTree[T] <: Tree[T] + type ThisTree[T >: Untyped] <: Tree[T] private var _tpe: T = _ @@ -77,6 +100,11 @@ object Trees { */ final def hasType: Boolean = _tpe != null + final def typeOpt: Type = _tpe match { + case tp: Type => tp + case _ => NoType + } + /** The denotation referred to by this tree. * Defined for `DenotingTree`s and `ProxyTree`s, NoDenotation for other * kinds of trees @@ -107,43 +135,39 @@ object Trees { override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] } - class UnAssignedTypeException[T](tree: Tree[T]) extends RuntimeException { + class UnAssignedTypeException[T >: Untyped](tree: Tree[T]) extends RuntimeException { override def getMessage: String = s"type of $tree is not assigned" } - type Untyped = Null - type TypedTree = Tree[Type] - type UntypedTree = Tree[Nothing] - // ------ 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] extends Tree[T] { - type ThisTree[T] <: TypTree[T] + 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] extends Tree[T] { - type ThisTree[T] <: TermTree[T] + 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] extends Tree[T] { - type ThisTree[T] <: PatternTree[T] + 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] extends Tree[T] { - type ThisTree[T] <: DenotingTree[T] + 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 _ => NoDenotation @@ -153,8 +177,8 @@ object Trees { /** Tree's denot/isType/isTerm properties come from a subtree * identified by `forwardTo`. */ - abstract class ProxyTree[T] extends Tree[T] { - type ThisTree[T] <: ProxyTree[T] + 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 @@ -162,146 +186,152 @@ object Trees { } /** Tree has a name */ - abstract class NameTree[T] extends DenotingTree[T] { - type ThisTree[T] <: NameTree[T] + 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] extends NameTree[T] { - type ThisTree[T] <: RefTree[T] + 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] extends DenotingTree[T] { - type ThisTree[T] <: DefTree[T] + trait DefTree[T >: Untyped] extends DenotingTree[T] { + type ThisTree[T >: Untyped] <: DefTree[T] override def isDef = true } // ----------- Tree case classes ------------------------------------ /** name */ - case class Ident[T](name: Name)(implicit cpos: Position) + case class Ident[T >: Untyped](name: Name)(implicit cpos: Position) extends RefTree[T] { - type ThisTree[T] = Ident[T] + type ThisTree[T >: Untyped] = Ident[T] val pos = cpos def qualifier: Tree[T] = EmptyTree[T] } + class BackquotedIdent[T >: Untyped](name: Name)(implicit cpos: Position) + extends Ident[T](name) + /** qualifier.name */ - case class Select[T](qualifier: Tree[T], name: Name)(implicit cpos: Position) + case class Select[T >: Untyped](qualifier: Tree[T], name: Name)(implicit cpos: Position) extends RefTree[T] { - type ThisTree[T] = Select[T] + type ThisTree[T >: Untyped] = Select[T] val pos = cpos union qualifier.pos } /** qual.this */ - case class This[T](qual: TypeName)(implicit cpos: Position) + case class This[T >: Untyped](qual: TypeName)(implicit cpos: Position) extends DenotingTree[T] with TermTree[T] { - type ThisTree[T] = This[T] + type ThisTree[T >: Untyped] = This[T] val pos = cpos } /** C.super[mix], where qual = C.this */ - case class Super[T](qual: Tree[T], mix: TypeName)(implicit cpos: Position) + case class Super[T >: Untyped](qual: Tree[T], mix: TypeName)(implicit cpos: Position) extends ProxyTree[T] with TermTree[T] { - type ThisTree[T] = Super[T] + type ThisTree[T >: Untyped] = Super[T] val pos = cpos union qual.pos def forwardTo = qual } - abstract class GenericApply[T] extends ProxyTree[T] with TermTree[T] { - type ThisTree[T] <: GenericApply[T] + 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](fun: Tree[T], args: List[Tree[T]])(implicit cpos: Position) + case class Apply[T >: Untyped](fun: Tree[T], args: List[Tree[T]])(implicit cpos: Position) extends GenericApply[T] { - type ThisTree[T] = Apply[T] + type ThisTree[T >: Untyped] = Apply[T] val pos = unionPos(cpos union fun.pos, args) } /** fun[args] */ - case class TypeApply[T](fun: Tree[T], args: List[Tree[T]])(implicit cpos: Position) + case class TypeApply[T >: Untyped](fun: Tree[T], args: List[Tree[T]])(implicit cpos: Position) extends GenericApply[T] { - type ThisTree[T] = TypeApply[T] + type ThisTree[T >: Untyped] = TypeApply[T] val pos = unionPos(cpos union fun.pos, args) } /** const */ - case class Literal[T](const: Constant)(implicit cpos: Position) + case class Literal[T >: Untyped](const: Constant)(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Literal[T] + type ThisTree[T >: Untyped] = Literal[T] val pos = cpos } /** new tpt, but no constructor call */ - case class New[T](tpt: Tree[T])(implicit cpos: Position) + case class New[T >: Untyped](tpt: Tree[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = New[T] + type ThisTree[T >: Untyped] = New[T] val pos = cpos union tpt.pos } /** (left, right) */ - case class Pair[T](left: Tree[T], right: Tree[T])(implicit cpos: Position) + case class Pair[T >: Untyped](left: Tree[T], right: Tree[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Pair[T] + type ThisTree[T >: Untyped] = Pair[T] val pos = cpos union left.pos union right.pos + override def isTerm = left.isTerm && right.isTerm + override def isType = left.isType && right.isType + override def isPattern = !isTerm && (left.isPattern || left.isTerm) && (right.isPattern || right.isTerm) } /** expr : tpt */ - case class Typed[T](expr: Tree[T], tpt: Tree[T])(implicit cpos: Position) + case class Typed[T >: Untyped](expr: Tree[T], tpt: Tree[T])(implicit cpos: Position) extends ProxyTree[T] with TermTree[T] { - type ThisTree[T] = Typed[T] + type ThisTree[T >: Untyped] = Typed[T] val pos = cpos union expr.pos union tpt.pos def forwardTo = expr } /** name = arg, in a parameter list */ - case class NamedArg[T](name: Name, arg: Tree[T])(implicit cpos: Position) + case class NamedArg[T >: Untyped](name: Name, arg: Tree[T])(implicit cpos: Position) extends Tree[T] { - type ThisTree[T] = NamedArg[T] + type ThisTree[T >: Untyped] = NamedArg[T] val pos = cpos union arg.pos } /** name = arg, outside a parameter list */ - case class Assign[T](lhs: Tree[T], rhs: Tree[T])(implicit cpos: Position) + case class Assign[T >: Untyped](lhs: Tree[T], rhs: Tree[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Assign[T] + type ThisTree[T >: Untyped] = Assign[T] val pos = cpos union lhs.pos union rhs.pos } /** { stats; expr } */ - case class Block[T](stats: List[Tree[T]], expr: Tree[T])(implicit cpos: Position) + case class Block[T >: Untyped](stats: List[Tree[T]], expr: Tree[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Block[T] + type ThisTree[T >: Untyped] = Block[T] val pos = unionPos(cpos union expr.pos, stats) } /** if cond then thenp else elsep */ - case class If[T](cond: Tree[T], thenp: Tree[T], elsep: Tree[T])(implicit cpos: Position) + case class If[T >: Untyped](cond: Tree[T], thenp: Tree[T], elsep: Tree[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = If[T] + type ThisTree[T >: Untyped] = If[T] val pos = cpos union cond.pos union thenp.pos union elsep.pos } /** selector match { cases } */ - case class Match[T](selector: Tree[T], cases: List[CaseDef[T]])(implicit cpos: Position) + case class Match[T >: Untyped](selector: Tree[T], cases: List[CaseDef[T]])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Match[T] + type ThisTree[T >: Untyped] = Match[T] val pos = unionPos(cpos union selector.pos, cases) } /** case pat if guard => body */ - case class CaseDef[T](pat: Tree[T], guard: Tree[T], body: Tree[T])(implicit cpos: Position) + case class CaseDef[T >: Untyped](pat: Tree[T], guard: Tree[T], body: Tree[T])(implicit cpos: Position) extends Tree[T] { - type ThisTree[T] = CaseDef[T] + type ThisTree[T >: Untyped] = CaseDef[T] val pos = cpos union pat.pos union guard.pos union body.pos } @@ -310,151 +340,151 @@ object Trees { * After program transformations this is not necessarily the enclosing method, because * closures can intervene. */ - case class Return[T](expr: Tree[T], from: Ident[T])(implicit cpos: Position) + case class Return[T >: Untyped](expr: Tree[T], from: Ident[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Return[T] + type ThisTree[T >: Untyped] = Return[T] val pos = cpos union expr.pos // from is synthetic, does not influence pos } /** try block catch { catches } */ - case class Try[T](block: Tree[T], catches: List[CaseDef[T]], finalizer: Tree[T])(implicit cpos: Position) + case class Try[T >: Untyped](block: Tree[T], catches: List[CaseDef[T]], finalizer: Tree[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Try[T] + type ThisTree[T >: Untyped] = Try[T] val pos = unionPos(cpos union block.pos union finalizer.pos, catches) } /** throw expr */ - case class Throw[T](expr: Tree[T])(implicit cpos: Position) + case class Throw[T >: Untyped](expr: Tree[T])(implicit cpos: Position) extends TermTree[T] { - type ThisTree[T] = Throw[T] + type ThisTree[T >: Untyped] = Throw[T] val pos = cpos union expr.pos } /** Array[elemtpt](elems) */ - case class SeqLiteral[T](elemtpt: Tree[T], elems: List[Tree[T]])(implicit cpos: Position) + case class SeqLiteral[T >: Untyped](elemtpt: Tree[T], elems: List[Tree[T]])(implicit cpos: Position) extends Tree[T] { - type ThisTree[T] = SeqLiteral[T] + type ThisTree[T >: Untyped] = SeqLiteral[T] val pos = unionPos(cpos union elemtpt.pos, elems) } /** A type tree that represents an existing or inferred type */ - case class TypeTree[T](original: Tree[T] = EmptyTree[T])(implicit cpos: Position) + case class TypeTree[T >: Untyped](original: Tree[T] = EmptyTree[T])(implicit cpos: Position) extends DenotingTree[T] with TypTree[T] { - type ThisTree[T] = TypeTree[T] - val pos = cpos union original.pos + type ThisTree[T >: Untyped] = TypeTree[T] + val pos = if (original.pos.exists) original.pos else cpos.focus } /** ref.type */ - case class SingletonTypeTree[T](ref: Tree[T])(implicit cpos: Position) + case class SingletonTypeTree[T >: Untyped](ref: Tree[T])(implicit cpos: Position) extends DenotingTree[T] with TypTree[T] { - type ThisTree[T] = SingletonTypeTree[T] + type ThisTree[T >: Untyped] = SingletonTypeTree[T] val pos = cpos union ref.pos } /** qualifier # name */ - case class SelectFromTypeTree[T](qualifier: Tree[T], name: Name)(implicit cpos: Position) + case class SelectFromTypeTree[T >: Untyped](qualifier: Tree[T], name: Name)(implicit cpos: Position) extends RefTree[T] { - type ThisTree[T] = SelectFromTypeTree[T] + type ThisTree[T >: Untyped] = SelectFromTypeTree[T] val pos = cpos union qualifier.pos } /** left & right */ - case class AndTypeTree[T](left: Tree[T], right: Tree[T])(implicit cpos: Position) + case class AndTypeTree[T >: Untyped](left: Tree[T], right: Tree[T])(implicit cpos: Position) extends TypTree[T] { - type ThisTree[T] = AndTypeTree[T] + type ThisTree[T >: Untyped] = AndTypeTree[T] val pos = cpos union left.pos union right.pos } /** left | right */ - case class OrTypeTree[T](left: Tree[T], right: Tree[T])(implicit cpos: Position) + case class OrTypeTree[T >: Untyped](left: Tree[T], right: Tree[T])(implicit cpos: Position) extends TypTree[T] { - type ThisTree[T] = OrTypeTree[T] + type ThisTree[T >: Untyped] = OrTypeTree[T] val pos = cpos union left.pos union right.pos } /** tpt { refinements } */ - case class RefineTypeTree[T](tpt: Tree[T], refinements: List[DefTree[T]])(implicit cpos: Position) + case class RefineTypeTree[T >: Untyped](tpt: Tree[T], refinements: List[DefTree[T]])(implicit cpos: Position) extends ProxyTree[T] with TypTree[T] { - type ThisTree[T] = RefineTypeTree[T] + type ThisTree[T >: Untyped] = RefineTypeTree[T] val pos = unionPos(cpos union tpt.pos, refinements) def forwardTo = tpt } /** tpt[args] */ - case class AppliedTypeTree[T](tpt: Tree[T], args: List[Tree[T]])(implicit cpos: Position) + case class AppliedTypeTree[T >: Untyped](tpt: Tree[T], args: List[Tree[T]])(implicit cpos: Position) extends ProxyTree[T] with TypTree[T] { - type ThisTree[T] = AppliedTypeTree[T] + type ThisTree[T >: Untyped] = AppliedTypeTree[T] val pos = unionPos(cpos union tpt.pos, args) def forwardTo = tpt } /** >: lo <: hi */ - case class TypeBoundsTree[T](lo: Tree[T], hi: Tree[T])(implicit cpos: Position) + case class TypeBoundsTree[T >: Untyped](lo: Tree[T], hi: Tree[T])(implicit cpos: Position) extends Tree[T] { - type ThisTree[T] = TypeBoundsTree[T] + type ThisTree[T >: Untyped] = TypeBoundsTree[T] val pos = cpos union lo.pos union hi.pos } /** name @ body */ - case class Bind[T](name: Name, body: Tree[T])(implicit cpos: Position) + case class Bind[T >: Untyped](name: Name, body: Tree[T])(implicit cpos: Position) extends NameTree[T] with DefTree[T] with PatternTree[T] { - type ThisTree[T] = Bind[T] + type ThisTree[T >: Untyped] = Bind[T] val pos = cpos union body.pos } /** tree_1 | ... | tree_n */ - case class Alternative[T](trees: List[Tree[T]])(implicit cpos: Position) + case class Alternative[T >: Untyped](trees: List[Tree[T]])(implicit cpos: Position) extends PatternTree[T] { - type ThisTree[T] = Alternative[T] + type ThisTree[T >: Untyped] = Alternative[T] val pos = unionPos(cpos, trees) } /** fun(args) in a pattern, if fun is an extractor */ - case class UnApply[T](fun: Tree[T], args: List[Tree[T]])(implicit cpos: Position) + case class UnApply[T >: Untyped](fun: Tree[T], args: List[Tree[T]])(implicit cpos: Position) extends PatternTree[T] { - type ThisTree[T] = UnApply[T] + type ThisTree[T >: Untyped] = UnApply[T] val pos = unionPos(cpos union fun.pos, args) } /** mods val name: tpt = rhs */ - case class ValDef[T](mods: Modifiers[T], name: TermName, tpt: Tree[T], rhs: Tree[T])(implicit cpos: Position) + case class ValDef[T >: Untyped](mods: Modifiers[T], name: TermName, tpt: Tree[T], rhs: Tree[T])(implicit cpos: Position) extends NameTree[T] with DefTree[T] { - type ThisTree[T] = ValDef[T] + type ThisTree[T >: Untyped] = ValDef[T] val pos = cpos union tpt.pos union rhs.pos } /** mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs */ - case class DefDef[T](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T])(implicit cpos: Position) + case class DefDef[T >: Untyped](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T])(implicit cpos: Position) extends NameTree[T] with DefTree[T] { - type ThisTree[T] = DefDef[T] + type ThisTree[T >: Untyped] = DefDef[T] val pos = (unionPos(cpos union tpt.pos union rhs.pos, tparams) /: vparamss)(unionPos) } - class ImplicitDefDef[T](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T])(implicit pos: Position) extends DefDef[T](mods, name, tparams, vparamss, tpt, rhs) { - override def copy[T](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T])(implicit pos: Position) = + class ImplicitDefDef[T >: Untyped](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T])(implicit pos: Position) extends DefDef[T](mods, name, tparams, vparamss, tpt, rhs) { + override def copy[T >: Untyped](mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T])(implicit pos: Position) = new ImplicitDefDef[T](mods, name, tparams, vparamss, tpt, rhs) } /** mods type name = rhs or * mods type name >: lo <: hi, if rhs = TypeBoundsTree(lo, hi) */ - case class TypeDef[T](mods: Modifiers[T], name: TypeName, rhs: Tree[T])(implicit cpos: Position) + case class TypeDef[T >: Untyped](mods: Modifiers[T], name: TypeName, rhs: Tree[T])(implicit cpos: Position) extends NameTree[T] with DefTree[T] { - type ThisTree[T] = TypeDef[T] + type ThisTree[T >: Untyped] = TypeDef[T] val pos = cpos union rhs.pos } /** extends parents { self => body } */ - case class Template[T](parents: List[Tree[T]], self: ValDef[T], body: List[Tree[T]])(implicit cpos: Position) + case class Template[T >: Untyped](parents: List[Tree[T]], self: ValDef[T], body: List[Tree[T]])(implicit cpos: Position) extends DefTree[T] { - type ThisTree[T] = Template[T] + type ThisTree[T >: Untyped] = Template[T] val pos = unionPos(unionPos(cpos union self.pos, parents), body) } /** mods class name[tparams] impl */ - case class ClassDef[T](mods: Modifiers[T], name: TypeName, tparams: List[TypeDef[T]], impl: Template[T])(implicit cpos: Position) + case class ClassDef[T >: Untyped](mods: Modifiers[T], name: TypeName, tparams: List[TypeDef[T]], impl: Template[T])(implicit cpos: Position) extends NameTree[T] with DefTree[T] { - type ThisTree[T] = ClassDef[T] + type ThisTree[T >: Untyped] = ClassDef[T] val pos = unionPos(cpos union impl.pos, tparams) } @@ -462,29 +492,29 @@ object Trees { * where a selector is either an untyped `Ident`, `name` or * an untyped `Pair` `name => rename` */ - case class Import[T](expr: Tree[T], selectors: List[UntypedTree])(implicit cpos: Position) + case class Import[T >: Untyped](expr: Tree[T], selectors: List[UntypedTree])(implicit cpos: Position) extends DenotingTree[T] { - type ThisTree[T] = Import[T] + type ThisTree[T >: Untyped] = Import[T] val pos = unionPos(cpos union expr.pos, selectors) } /** package pid { stats } */ - case class PackageDef[T](pid: RefTree[T], stats: List[Tree[T]])(implicit cpos: Position) + case class PackageDef[T >: Untyped](pid: RefTree[T], stats: List[Tree[T]])(implicit cpos: Position) extends ProxyTree[T] { - type ThisTree[T] = PackageDef[T] + type ThisTree[T >: Untyped] = PackageDef[T] val pos = unionPos(cpos union pid.pos, stats) def forwardTo = pid } /** arg @annot */ - case class Annotated[T](annot: Tree[T], arg: Tree[T])(implicit cpos: Position) + case class Annotated[T >: Untyped](annot: Tree[T], arg: Tree[T])(implicit cpos: Position) extends ProxyTree[T] { - type ThisTree[T] = Annotated[T] + type ThisTree[T >: Untyped] = Annotated[T] val pos = cpos union annot.pos union arg.pos def forwardTo = arg } - trait AlwaysEmpty[T] extends Tree[T] { + trait AlwaysEmpty[T >: Untyped] extends Tree[T] { override val pos = NoPosition override def tpe = unsupported("tpe") override def withType(tpe: Type) = unsupported("withType") @@ -492,32 +522,32 @@ object Trees { } /** A missing tree */ - abstract case class EmptyTree[T]() + abstract case class EmptyTree[T >: Untyped]() extends Tree[T] with AlwaysEmpty[T] { - type ThisTree[T] = EmptyTree[T] + type ThisTree[T >: Untyped] = EmptyTree[T] } - private object theEmptyTree extends EmptyTree[Nothing] + private object theEmptyTree extends EmptyTree[Untyped] object EmptyTree { - def apply[T]: EmptyTree[T] = theEmptyTree.asInstanceOf[EmptyTree[T]] + def apply[T >: Untyped](): EmptyTree[T] = theEmptyTree.asInstanceOf[EmptyTree[T]] } - class EmptyValDef[T] extends ValDef[T]( + class EmptyValDef[T >: Untyped] extends ValDef[T]( Modifiers[T](Private), nme.WILDCARD, EmptyTree[T], EmptyTree[T])(NoPosition) with AlwaysEmpty[T] - private object theEmptyValDef extends EmptyValDef[Nothing] + private object theEmptyValDef extends EmptyValDef[Untyped] object EmptyValDef { - def apply[T]: EmptyValDef[T] = theEmptyValDef.asInstanceOf[EmptyValDef[T]] + def apply[T >: Untyped](): EmptyValDef[T] = theEmptyValDef.asInstanceOf[EmptyValDef[T]] } /** A tree that can be shared without its position * polluting containing trees. Accumulators and tranformers * memoize results of shared subtrees */ - case class SharedTree[T](shared: Tree[T]) extends ProxyTree[T] { - type ThisTree[T] = SharedTree[T] + case class SharedTree[T >: Untyped](shared: Tree[T]) extends ProxyTree[T] { + type ThisTree[T >: Untyped] = SharedTree[T] def forwardTo: Tree[T] = shared val pos = NoPosition } @@ -525,36 +555,47 @@ object Trees { // ----- Tree cases that exist in untyped form only ------------------ /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice */ - class TypedSplice(tree: TypedTree) extends UntypedTree { + case class TypedSplice(tree: TypedTree) extends UntypedTree { val pos = tree.pos } /** mods object name impl */ - case class ModuleDef(mods: Modifiers[Nothing], name: TermName, impl: Template[Nothing])(implicit cpos: Position) - extends NameTree[Nothing] with DefTree[Nothing] { - type ThisTree[T] <: NameTree[T] with DefTree[T] with ModuleDef + case class ModuleDef(mods: Modifiers[Untyped], name: TermName, impl: Template[Untyped])(implicit cpos: Position) + extends NameTree[Untyped] with DefTree[Untyped] { + type ThisTree[T >: Untyped] <: NameTree[T] with DefTree[T] with ModuleDef val pos = cpos union impl.pos - def derivedModuleDef(mods: Modifiers[Nothing], name: TermName, impl: Template[Nothing]) = + def derivedModuleDef(mods: Modifiers[Untyped], name: TermName, impl: Template[Untyped]) = if (mods == this.mods && name == this.name && (impl eq this.impl)) this else ModuleDef(mods, name, impl) } /** (vparams) => body */ - case class Function(vparams: List[ValDef[Nothing]], body: Tree[Nothing])(implicit cpos: Position) - extends TermTree[Nothing] { - type ThisTree[T] <: TermTree[T] with Function + case class Function(vparams: List[ValDef[Untyped]], body: Tree[Untyped])(implicit cpos: Position) + extends TermTree[Untyped] { + type ThisTree[T >: Untyped] <: TermTree[T] with Function val pos = unionPos(cpos union body.pos, vparams) } /** Something in parentheses */ - case class Parens(trees: List[Tree[Nothing]])(implicit cpos: Position) extends Tree[Nothing] { - type ThisType[T] <: Parens + case class Parens(trees: List[Tree[Untyped]])(implicit cpos: Position) extends Tree[Untyped] { + type ThisTree[T >: Untyped] <: Tree[T] with Parens val pos = unionPos(cpos, trees) } + // ----- Auxiliary creation methods ------------------ + + def Block[T >: Untyped](stat: Tree[T], expr: Tree[T])(implicit cpos: Position): Block[T] = + Block(stat :: Nil, expr) + + def Apply[T >: Untyped](fn: Tree[T], arg: Tree[T])(implicit cpos: Position): Apply[T] = + Apply(fn, arg :: Nil) + + def Function(vparam: ValDef[Untyped], body: Tree[Untyped])(implicit cpos: Position): Function = + Function(vparam :: Nil, body) + // ----- Generic Tree Instances, inherited from `tpt` and `untpd`. - abstract class Instance[T] { + abstract class Instance[T >: Untyped] { type Modifiers = Trees.Modifiers[T] type Tree = Trees.Tree[T] @@ -625,11 +666,14 @@ object Trees { case nil => base } - implicit class TreeCopier[T](val tree: Tree[T]) extends AnyVal { + implicit class TreeCopier[T >: Untyped](val tree: Tree[T]) extends AnyVal { implicit def cpos = tree.pos def derivedIdent(name: Name): Ident[T] = tree match { + case tree: BackquotedIdent[_] => + if (name == tree.name) tree + else new BackquotedIdent[T](name).copyAttr(tree) case tree: Ident[_] if (name == tree.name) => tree - case _ => Ident(name).copyAttr(tree) + case _ => Ident[T](name).copyAttr(tree) } def derivedSelect(qualifier: Tree[T], name: Name): Select[T] = tree match { case tree: Select[_] if (qualifier eq tree.qualifier) && (name == tree.name) => tree @@ -637,7 +681,7 @@ object Trees { } def derivedThis(qual: TypeName): This[T] = tree match { case tree: This[_] if (qual == tree.qual) => tree - case _ => This(qual).copyAttr(tree) + case _ => This[T](qual).copyAttr(tree) } def derivedSuper(qual: Tree[T], mix: TypeName): Super[T] = tree match { case tree: Super[_] if (qual eq tree.qual) && (mix == tree.mix) => tree @@ -653,7 +697,7 @@ object Trees { } def derivedLiteral(const: Constant): Literal[T] = tree match { case tree: Literal[_] if (const == tree.const) => tree - case _ => Literal(const).copyAttr(tree) + case _ => Literal[T](const).copyAttr(tree) } def derivedNew(tpt: Tree[T]): New[T] = tree match { case tree: New[_] if (tpt eq tree.tpt) => tree @@ -789,7 +833,7 @@ object Trees { } } - abstract class FullTreeTransformer[T, C] { + abstract class FullTreeTransformer[T >: Untyped, C] { var sharedMemo: Map[SharedTree[T], SharedTree[T]] = Map() def transform(tree: Tree[T], c: C): Tree[T] = tree match { @@ -938,7 +982,7 @@ object Trees { def finishSharedTree(tree: Tree[T], old: Tree[T], c: C, plugins: Plugins) = tree } - abstract class TreeTransformer[T] { + abstract class TreeTransformer[T >: Untyped] { var sharedMemo: Map[SharedTree[T], SharedTree[T]] = Map() def transform(tree: Tree[T]): Tree[T] = tree match { @@ -1039,11 +1083,11 @@ object Trees { transform(trees).asInstanceOf[List[TT]] } - abstract class TreeAccumulator[T, U] extends ((T, Tree[U]) => T) { - var sharedMemo: Map[SharedTree[U], T] = Map() - def apply(x: T, tree: Tree[U]): T - def apply(x: T, trees: List[Tree[U]]): T = (x /: trees)(apply) - def foldOver(x: T, tree: Tree[U]): T = tree match { + abstract class TreeAccumulator[X, T >: Untyped] extends ((X, Tree[T]) => X) { + var sharedMemo: Map[SharedTree[T], X] = Map() + def apply(x: X, tree: Tree[T]): X + def apply(x: X, trees: List[Tree[T]]): X = (x /: trees)(apply) + def foldOver(x: X, tree: Tree[T]): X = tree match { case Ident(name) => x case Select(qualifier, name) => @@ -1136,15 +1180,15 @@ object Trees { } /** Fold `f` over all tree nodes, in depth-first, prefix order */ - class DeepFolder[T, U](f: (T, Tree[U]) => T) extends TreeAccumulator[T, U] { - def apply(x: T, tree: Tree[U]): T = foldOver(f(x, tree), tree) + class DeepFolder[X, T >: Untyped](f: (X, Tree[T]) => X) extends TreeAccumulator[X, T] { + def apply(x: X, tree: Tree[T]): 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[T, U](f: (T, Tree[U]) => T) extends TreeAccumulator[T, U] { - def apply(x: T, tree: Tree[U]): T = { + class ShallowFolder[X, T >: Untyped](f: (X, Tree[T]) => X) extends TreeAccumulator[X, T] { + def apply(x: X, tree: Tree[T]): X = { val x1 = f(x, tree) if (x1.asInstanceOf[AnyRef] ne x1.asInstanceOf[AnyRef]) x1 else foldOver(x1, tree) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 133018a96..dc61195b5 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -139,6 +139,11 @@ object Types { final def isVolatile(implicit ctx: Context): Boolean = ctx.isVolatile(this) + final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = this match { + case AnnotatedType(annot, tp) => annot.symbol == cls || tp.hasAnnotation(cls) + case _ => false + } + // ----- Higher-order combinators ----------------------------------- /** Returns true if there is a part of this type that satisfies predicate `p`. @@ -1293,6 +1298,8 @@ object Types { def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = apply(paramNames, paramTypes)(_ => resultType) + def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = + apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { def transformResult(mt: MethodType) = resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _))) diff --git a/src/dotty/tools/dotc/core/UntypedTrees.scala b/src/dotty/tools/dotc/core/UntypedTrees.scala index c882b4982..2b1d6a0d6 100644 --- a/src/dotty/tools/dotc/core/UntypedTrees.scala +++ b/src/dotty/tools/dotc/core/UntypedTrees.scala @@ -2,11 +2,12 @@ package dotty.tools.dotc package core import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ -import SymDenotations._, Symbols._, StdNames._, Annotations._ +import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ object UntypedTrees { - object untpd extends Trees.Instance[Nothing] { + object untpd extends Trees.Instance[Untyped] { + } } diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index ee8d9de83..f817cf9b9 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -946,7 +946,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: val toName = readNameRef() val from = Trees.Ident(fromName) val to = Trees.Ident(toName) - if (toName.isEmpty) from else Trees.Pair[Nothing](from, Trees.Ident(toName)) + if (toName.isEmpty) from else Trees.Pair(from, Trees.Ident(toName)) }) Import(expr, selectors) diff --git a/src/dotty/tools/dotc/parsing/TreeBuilder.scala b/src/dotty/tools/dotc/parsing/TreeBuilder.scala new file mode 100644 index 000000000..2f415e6a5 --- /dev/null +++ b/src/dotty/tools/dotc/parsing/TreeBuilder.scala @@ -0,0 +1,548 @@ +package dotty.tools +package dotc +package parsing + +import core._ +import Flags._, Trees._, TypedTrees._, UntypedTrees._, Names._, StdNames._, NameOps._, Contexts._ +import scala.collection.mutable.ListBuffer +import util.Positions._, Symbols._, Decorators._, Flags._, Constants._ + +/** Methods for building trees, used in the parser. All the trees + * returned by this class must be untyped. + */ +abstract class TreeBuilder(implicit ctx: Context) { + + import untpd._ + + def o2p(offset: Int): Position + def r2p(start: Int, point: Int, end: Int): Position + + def scalaDot(name: Name)(implicit cpos: Position): Select = + Select(new TypedSplice(tpd.Ident(defn.ScalaPackageVal.termRef)), name) + + def scalaAnyRefConstr(implicit cpos: Position) = scalaDot(tpnme.AnyRef) + def scalaAnyValConstr(implicit cpos: Position) = scalaDot(tpnme.AnyVal) + def scalaAnyConstr(implicit cpos: Position) = scalaDot(tpnme.Any) + def scalaUnitConstr(implicit cpos: Position) = scalaDot(tpnme.Unit) + def productConstr(implicit cpos: Position) = scalaDot(tpnme.Product) + def productConstrN(n: Int)(implicit cpos: Position) = scalaDot(("Product" + n).toTypeName) + def serializableConstr(implicit cpos: Position) = scalaDot(tpnme.Serializable) + + def convertToTypeName(t: Tree) = ??? + + implicit val cpos = NoPosition + + /** Convert all occurrences of (lower-case) variables in a pattern as follows: + * x becomes x @ _ + * x: T becomes x @ (_: T) + */ + private object patvarTransformer extends TreeTransformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(name) if (treeInfo.isVarPattern(tree) && name != nme.WILDCARD) => + Bind( + name, Ident(nme.WILDCARD)(tree.pos.focus) + )(tree.pos) + case Typed(id @ Ident(name), tpt) if (treeInfo.isVarPattern(id) && name != nme.WILDCARD) => + Bind( + name, + Typed( + Ident(nme.WILDCARD)(tree.pos.focus), + tpt + )(tree.pos.withStart(tree.pos.point)) + )(tree.pos.withPoint(id.pos.point)) + case Apply(fn @ Apply(_, _), args) => + tree.derivedApply(transform(fn), transform(args)) + case Apply(fn, args) => + tree.derivedApply(fn, transform(args)) + case Typed(expr, tpt) => + tree.derivedTyped(transform(expr), tpt) + case Bind(name, body) => + tree.derivedBind(name, transform(body)) + case Alternative(_) => + super.transform(tree) + case _ => + tree + } + } + +case class VariableInfo(name: Name, tree: Tree, pos: Position) + + /** Traverse pattern and collect all variable names with their types in buffer + * The variables keep their positions; whereas the pattern is converted to be + * synthetic for all nodes that contain a variable position. + */ + object getVars extends TreeAccumulator[ListBuffer[VariableInfo]] { + + def namePos(tree: Tree, name: Name): Position = + if (name contains '$') tree.pos.focus + else { + val start = tree.pos.start + val end = start + name.decode.length + Position(start, end) + } + + override def apply(buf: ListBuffer[VariableInfo], tree: Tree): ListBuffer[VariableInfo] = { + def seenName(name: Name) = buf exists (_.name == name) + def add(name: Name, t: Tree): ListBuffer[VariableInfo] = + if (seenName(name)) buf else buf += VariableInfo(name, t, namePos(tree, name)) + + tree match { + case Bind(nme.WILDCARD, _) => + foldOver(buf, tree) + case Bind(name, Typed(tree1, tpt)) if !treeInfo.mayBeTypePat(tpt) => + apply(add(name, tpt), tree1) + case Bind(name, tree1) => + apply(add(name, TypeTree()), tree1) + case _ => + foldOver(buf, tree) + } + } + } + + /** Returns list of all pattern variables, possibly with their types, + * without duplicates + */ + private def getVariables(tree: Tree): List[VariableInfo] = + getVars(new ListBuffer[VariableInfo], tree).toList + + def byNameApplication(tpe: Tree)(implicit cpos: Position): Tree = + AppliedTypeTree(scalaDot(tpnme.BYNAME_PARAM_CLASS), List(tpe)) + def repeatedApplication(tpe: Tree)(implicit cpos: Position): Tree = + AppliedTypeTree(scalaDot(tpnme.REPEATED_PARAM_CLASS), List(tpe)) + + private def makeTuple(trees: List[Tree])(implicit cpos: Position): Tree = { + def mkPair(t1: Tree, t2: Tree) = Pair(t1, t2)(Position(t1.pos.start, cpos.end)) + trees reduce mkPair + } + + def stripParens(t: Tree) = t match { + case Parens(ts) => makeTuple(ts)(t.pos) + case _ => t + } + + def makeSelfDef(name: TermName, tpt: Tree)(implicit cpos: Position): ValDef = + ValDef(Modifiers(Private), name, tpt, EmptyTree()) + + /** If tree is a variable pattern, return Some("its name and type"). + * Otherwise return none */ + private def matchVarPattern(tree: Tree): Option[(Name, Tree)] = { + def wildType(t: Tree): Option[Tree] = t match { + case Ident(x) if x.toTermName == nme.WILDCARD => Some(TypeTree()) + case Typed(Ident(x), tpt) if x.toTermName == nme.WILDCARD => Some(tpt) + case _ => None + } + tree match { + case Ident(name) => Some((name, TypeTree())) + case Bind(name, body) => wildType(body) map (x => (name, x)) + case Typed(Ident(name), tpt) => Some((name, tpt)) + case _ => None + } + } + + /** Create tree representing (unencoded) binary operation expression or pattern. */ + def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = { + def mkNamed(args: List[Tree]) = + if (isExpr) args map { + case arg @ Assign(Ident(name), rhs) => NamedArg(name, rhs)(arg.pos) + case arg => arg + } else args + val arguments = right match { + case Parens(args) => mkNamed(args) + case _ => right :: Nil + } + if (isExpr) { + if (treeInfo.isLeftAssoc(op)) { + Apply(Select(stripParens(left), op.encode)(opPos), arguments) + } else { + val x = ctx.freshName().toTermName + Block( + List(ValDef(Modifiers(Synthetic), x, TypeTree(), stripParens(left))), + Apply(Select(stripParens(right), op.encode)(opPos), List(Ident(x)(left.pos)))) + } + } else { + Apply(Ident(op.encode)(opPos), stripParens(left) :: arguments) + } + } + + /** Creates a tree representing new Object { stats }. + * To make sure an anonymous subclass of Object is created, + * if there are no stats, a () is added. + */ + def makeAnonymousNew(stats: List[Tree])(implicit cpos: Position): Tree = { + val stats1 = if (stats.isEmpty) Literal(Constant(())) :: Nil else stats + makeNew(Nil, EmptyValDef(), stats1) + } + + /** tpt.<init> */ + def SelectConstructor(tpt: Tree)(implicit cpos: Position): Tree = + Select(tpt, nme.CONSTRUCTOR) + + def splitArgss(constr: Tree, outerArgss: List[List[Tree]]): (Tree, List[List[Tree]]) = constr match { + case Apply(tree, args) => splitArgss(tree, args :: outerArgss) + case _ => (constr, if (outerArgss.isEmpty) ListOfNil else outerArgss) + } + + /** new tpt(argss_1)...(argss_n) + * @param npos the position spanning <new tpt>, without any arguments + */ + def makeNew(parentConstr: Tree, npos: Position) = { + val (tpt, argss1) = splitArgss(parentConstr, Nil) + (SelectConstructor(tpt)(npos) /: argss1)(Apply(_, _)) + } + + /** Create positioned tree representing an object creation <new parents { self => stats } + * @param pos the position of the new, focus should be the first parent's start. + */ + def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree])(implicit cpos: Position): Tree = { + val newPos = Position(cpos.start, cpos.point) + val clsPos = Position(cpos.point, cpos.end) + if (parents.isEmpty) + makeNew(List(scalaAnyRefConstr(cpos.startPos)), self, stats) + else if (parents.tail.isEmpty && stats.isEmpty) + makeNew(parents.head, newPos) + else { + val x = tpnme.ANON_CLASS + val nu = makeNew(Ident(x)(newPos), newPos) + val clsDef = { + implicit val cpos = clsPos + ClassDef(Modifiers(Final), x, Nil, Template(parents, self, stats)) + } + Block(clsDef, nu) + } + } + + /** Create a tree representing an assignment <lhs = rhs> */ + def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match { + case Apply(fn, args) => + Apply(Select(fn, nme.update), args :+ rhs) + case _ => + Assign(lhs, rhs) + } + + /** A type tree corresponding to (possibly unary) intersection type + def makeIntersectionTypeTree(tps: List[Tree]): Tree = + if (tps.tail.isEmpty) tps.head + else CompoundTypeTree(Template(tps, emptyValDef, Nil))*/ + + private def labelDefAndCall(lname: TermName, rhs: Tree, call: Tree)(implicit cpos: Position) = { + val ldef = DefDef(Modifiers(Label), lname, Nil, ListOfNil, TypeTree(), rhs) + Block(ldef, call) + } + + private def labelCall(lname: TermName)(implicit cpos: Position): Apply = + Apply(Ident(lname), Nil) + + /** Create tree representing a while loop */ + def makeWhile(lname: TermName, cond: Tree, body: Tree)(implicit cpos: Position): Tree = { + val continu = labelCall(lname)((cond.pos union body.pos).endPos) + val rhs = { + implicit val cpos = NoPosition + If(cond, Block(body, continu), Literal(Constant())(continu.pos)) + } + labelDefAndCall(lname, rhs, continu) + } + + /** Create tree representing a do-while loop */ + def makeDoWhile(lname: TermName, body: Tree, cond: Tree)(implicit cpos: Position): Tree = { + val continu = labelCall(lname)((cond.pos union body.pos).endPos) + val rhs = Block(body, If(cond, continu, Literal(Constant())(continu.pos))) + labelDefAndCall(lname, rhs, continu) + } + + /** Create block of statements `stats` */ + def makeBlock(stats: List[Tree])(implicit cpos: Position): Tree = + if (stats.isEmpty) Literal(Constant()) + else if (!stats.last.isTerm) Block(stats, Literal(Constant())(cpos.endPos)) + else if (stats.length == 1) stats.head + else Block(stats.init, stats.last) + + def makeFilter(tree: Tree, condition: Tree, canDrop: Boolean): Tree = { + val cases = List( + CaseDef(condition, EmptyTree(), Literal(Constant(true))), + CaseDef(Ident(nme.WILDCARD), EmptyTree(), Literal(Constant(false))) + ) + val matchTree = makeVisitor(cases, checkExhaustive = false, canDrop) + locally { + implicit val cpos = tree.pos + Apply(Select(tree, nme.withFilter), matchTree :: Nil) + } + } + + /** Create tree for for-comprehension generator <pat <- rhs> or <pat = rhs> */ + def makeGenerator(pat: Tree, valeq: Boolean, rhs: Tree)(implicit cpos: Position): Enumerator = { + val pat1 = patvarTransformer.transform(pat) + if (valeq) ValEq(pat1, rhs)(cpos) + else ValFrom(pat1, makeFilter(rhs, pat1, canDrop = true))(cpos) + } + + def makeParam(pname: TermName, tpe: Tree)(implicit cpos: Position) = + ValDef(Modifiers(Param), pname, tpe, EmptyTree()) + + def makeSyntheticParam(pname: TermName)(implicit cpos: Position) = + ValDef(Modifiers(SyntheticTermParam), pname, TypeTree(), EmptyTree()) +/* + def makeSyntheticTypeParam(pname: TypeName, bounds: Tree) = + TypeDef(Modifiers(DEFERRED | SYNTHETIC), pname, Nil, bounds) +*/ + abstract class Enumerator { def pos: Position } + case class ValFrom(pat: Tree, rhs: Tree)(implicit cpos: Position) extends Enumerator { + val pos = cpos union pat.pos union rhs.pos + } + case class ValEq(pat: Tree, rhs: Tree)(implicit cpos: Position) extends Enumerator { + val pos = cpos union pat.pos union rhs.pos + } + case class Filter(test: Tree)(implicit cpos: Position) extends Enumerator { + val pos = cpos union test.pos + } + + /** 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 + */ + private def makeFor(mapName: TermName, flatMapName: TermName, enums: List[Enumerator], body: Tree): Tree = { + + /** make a closure pat => body. + * The closure is assigned a transparent position with the point at pos.point and + * the limits given by pat and body. + */ + def makeClosure(pat: Tree, body: Tree)(implicit cpos: Position): Tree = + matchVarPattern(pat) match { + case Some((name, tpt)) => + Function(ValDef(Modifiers(Param), name.toTermName, tpt, EmptyTree())(pat.pos), body) + case None => + makeVisitor(List(CaseDef(pat, EmptyTree(), body)), checkExhaustive = false) + } + + /** Make an application qual.meth(pat => body) positioned at `pos`. + */ + def makeCombination(meth: TermName, qual: Tree, pat: Tree, body: Tree)(implicit cpos: Position): Tree = + Apply(Select(qual, meth)(NoPosition), makeClosure(pat, body)) + + /** Optionally, if pattern is a `Bind`, the bound name, otherwise None. + */ + def patternVar(pat: Tree): Option[Name] = pat match { + case Bind(name, _) => Some(name) + case _ => None + } + + /** If `pat` is not yet a `Bind` wrap it in one with a fresh name + */ + def makeBind(pat: Tree): Tree = pat match { + case Bind(_, _) => pat + case _ => Bind(ctx.freshName().toTermName, pat) + } + + /** A reference to the name bound in Bind `pat`. + */ + def makeValue(pat: Tree): Tree = pat match { + case Bind(name, _) => Ident(name)(pat.pos.focus) + } + + enums match { + case (enum @ ValFrom(pat, rhs)) :: Nil => + makeCombination(mapName, rhs, pat, body)(enum.pos) + case ValFrom(pat, rhs) :: (rest @ (ValFrom( _, _) :: _)) => + makeCombination(flatMapName, rhs, pat, + makeFor(mapName, flatMapName, rest, body)) + case (enum @ ValFrom(pat, rhs)) :: Filter(test) :: rest => + makeFor(mapName, flatMapName, + ValFrom(pat, makeCombination(nme.withFilter, rhs, pat, test))(enum.pos) :: rest, + body) + case (enum @ ValFrom(pat, rhs)) :: rest => + val (valeqs, rest1) = rest.span(_.isInstanceOf[ValEq]) + assert(!valeqs.isEmpty) + val pats = valeqs map { case ValEq(pat, _) => pat } + val rhss = valeqs map { case ValEq(_, rhs) => rhs } + val defpat1 = makeBind(pat) + val defpats = pats map makeBind + val pdefs = (defpats, rhss).zipped flatMap makePatDef + val ids = (defpat1 :: defpats) map makeValue + val rhs1 = makeForYield(ValFrom(defpat1, rhs) :: Nil, Block(pdefs, makeTuple(ids))) + val allpats = pat :: pats + val vfrom1 = ValFrom(makeTuple(allpats), rhs1)(enum.pos) + makeFor(mapName, flatMapName, vfrom1 :: rest1, body) + case _ => + EmptyTree() //may happen for erroneous input + } + } + + /** Create tree for for-do comprehension <for (enums) body> */ + def makeFor(enums: List[Enumerator], body: Tree): Tree = + makeFor(nme.foreach, nme.foreach, enums, body) + + /** Create tree for for-yield comprehension <for (enums) yield body> */ + def makeForYield(enums: List[Enumerator], body: Tree): Tree = + makeFor(nme.map, nme.flatMap, enums, body) + + /** Create tree for a pattern alternative */ + def makeAlternative(ts: List[Tree]): Tree = { + def alternatives(t: Tree): List[Tree] = t match { + case Alternative(ts) => ts + case _ => List(t) + } + Alternative(ts flatMap alternatives) + } + + def mkAnnotated(cls: Symbol, tree: Tree) = + Annotated(TypedSplice(tpd.New(cls.typeConstructor)), tree) + + /** Create visitor <x => x match cases> */ + def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean, canDrop: Boolean = false): Tree = { + val x = ctx.freshName().toTermName + val id = Ident(x) + val sel = + if (canDrop) mkAnnotated(defn.DropIfRedundantAnnot, id) + else if (!checkExhaustive) mkAnnotated(defn.UncheckedAnnot, id) + else id + Function(List(makeSyntheticParam(x)), Match(sel, cases)) + } + + /** Create tree for case definition <case pat if guard => rhs> */ + def makeCaseDef(pat: Tree, guard: Tree, rhs: Tree): CaseDef = + CaseDef(patvarTransformer.transform(pat), guard, rhs) + + /** Creates tree representing: + * { case x: Throwable => + * val catchFn = catchExpr + * if (catchFn isDefinedAt x) catchFn(x) else throw x + * } + */ + def makeCatchFromExpr(catchExpr: Tree): CaseDef = { + implicit val cpos = catchExpr.pos.startPos + val binder = ctx.freshName("x").toTermName + val pat = Bind(binder, Typed(Ident(nme.WILDCARD), Ident(tpnme.Throwable))) + val catchDef = ValDef(Modifiers(), ctx.freshName("catchExpr").toTermName, TypeTree(), catchExpr) + val catchFn = Ident(catchDef.name) + val cond = Apply(Select(catchFn, nme.isDefinedAt), Ident(binder)) + val app = Apply(Select(catchFn, nme.apply), List(Ident(binder))) + val thro = Throw(Ident(binder)) + val body = Block(catchDef, If(cond, app, thro)) + makeCaseDef(pat, EmptyTree(), body) + } + + /** Create tree for pattern definition <val pat0 = rhs> */ + def makePatDef(pat: Tree, rhs: Tree): List[Tree] = + makePatDef(Modifiers(), pat, rhs) + + /** Create tree for pattern definition <mods val pat0 = rhs> */ + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree, varsArePatterns: Boolean = false): List[Tree] = matchVarPattern(pat) match { + case Some((name, tpt)) if varsArePatterns => + ValDef(mods, name.toTermName, tpt, rhs) :: Nil + + case _ => + // in case there is exactly one variable x_1 in pattern + // val/var p = e ==> val/var x_1 = e.match (case p => (x_1)) + // + // in case there are zero or more than one variables in pattern + // val/var p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + // val/var x_1 = t$._1 + // ... + // val/var x_N = t$._N + + val rhsUnchecked = mkAnnotated(defn.UncheckedAnnot, rhs) + + // TODO: clean this up -- there is too much information packed into makePatDef's `pat` argument + // when it's a simple identifier (case Some((name, tpt)) -- above), + // pat should have the type ascription that was specified by the user + // however, in `case None` (here), we must be careful not to generate illegal pattern trees (such as `(a, b): Tuple2[Int, String]`) + // i.e., this must hold: pat1 match { case Typed(expr, tp) => assert(expr.isInstanceOf[Ident]) case _ => } + // if we encounter such an erroneous pattern, we strip off the type ascription from pat and propagate the type information to rhs + val (pat1, rhs1) = patvarTransformer.transform(pat) match { + // move the Typed ascription to the rhs + case Typed(expr, tpt) if !expr.isInstanceOf[Ident] => + val rhsTypedUnchecked = + if (tpt.isEmpty) rhsUnchecked else Typed(rhsUnchecked, tpt) + (expr, rhsTypedUnchecked) + case ok => + (ok, rhsUnchecked) + } + val vars = getVariables(pat1) + val ids = vars map (v => Ident(v.name)(v.pos)) + val caseDef = CaseDef(pat1, EmptyTree(), makeTuple(ids)) + val matchExpr = Match(rhs1, caseDef :: Nil) + vars match { + case List(VariableInfo(vname, tpt, pos)) => + ValDef(mods, vname.toTermName, tpt, matchExpr) :: Nil + case _ => + val tmpName = ctx.freshName().toTermName + val patMods = Modifiers(PrivateLocal | Synthetic | (mods.flags & Lazy)) + val firstDef = ValDef(patMods, tmpName, TypeTree(), matchExpr) + val restDefs = for { + (VariableInfo(vname, tpt, pos), n) <- vars.zipWithIndex + } yield { + val rhs = { + implicit val cpos = pos.focus + Select(Ident(tmpName), ("_" + n).toTermName) + } + ValDef(mods, vname.toTermName, tpt, rhs)(pos) + } + firstDef :: restDefs + } + } + + /** Create a tree representing the function type (argtpes) => restpe */ + def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree(scalaDot(("Function" + argtpes.length).toTypeName), argtpes ::: List(restpe)) + + /** Append implicit parameter section if `contextBounds` nonempty */ + def addEvidenceParams(owner: Name, vparamss: List[List[ValDef]], contextBounds: List[Tree]): List[List[ValDef]] = { + if (contextBounds.isEmpty) vparamss + else { + val mods = Modifiers(if (owner.isTypeName) PrivateLocal | ParamAccessor else Param) + val evidenceParams = for (tpt <- contextBounds) yield { + val pname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX.toString).toTermName + ValDef(mods | Implicit | Synthetic, pname, tpt, EmptyTree()) + } + vparamss.reverse match { + case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit => + vparamss.init :+ (evidenceParams ++ vparams) + case _ => + vparamss :+ evidenceParams + } + } + } +} diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index 3e7eaecde..86dfb96d8 100644 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/src/dotty/tools/dotc/reporting/Reporter.scala @@ -30,13 +30,16 @@ trait Reporting { this: Context => " in " + (currentTimeMillis - start) + "ms" def informProgress(msg: => String) = - if (ctx.settings.verbose.value) inform("[" + msg + "]") + if (this.settings.verbose.value) inform("[" + msg + "]") def trace[T](msg: => String)(value: T) = { log(msg + " " + value) value } + def debugwarn(msg: String, pos: SourcePosition = NoSourcePosition): Unit = + if (this.settings.debug.value) warning(msg, pos) + def debugTraceIndented[T](question: => String)(op: => T): T = if (this.settings.debugTrace.value) traceIndented(question)(op) else op diff --git a/src/dotty/tools/dotc/util/Positions.scala b/src/dotty/tools/dotc/util/Positions.scala index 31eb2ff0d..bf7bc7414 100644 --- a/src/dotty/tools/dotc/util/Positions.scala +++ b/src/dotty/tools/dotc/util/Positions.scala @@ -40,6 +40,8 @@ object Positions { else this def focus = Position(point) + def startPos = Position(start) + def endPos = Position(end) def withStart(start: Int) = Position(start, this.end, this.point - start) def withEnd(end: Int) = Position(this.start, end, this.point - this.start) |