summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-07-16 05:44:02 +0000
committerPaul Phillips <paulp@improving.org>2011-07-16 05:44:02 +0000
commit207b5ef725cea2f1e7fe6c54a4f3c451f8c708f6 (patch)
treea9a641273e831f211282c2b4f09d8bd9ee03b310
parent4c71fabc01155dc4b6e06248fd904a71a9cdfebb (diff)
downloadscala-207b5ef725cea2f1e7fe6c54a4f3c451f8c708f6.tar.gz
scala-207b5ef725cea2f1e7fe6c54a4f3c451f8c708f6.tar.bz2
scala-207b5ef725cea2f1e7fe6c54a4f3c451f8c708f6.zip
A whole bunch of documentation on Trees, arrest...
A whole bunch of documentation on Trees, arrested in progress. (Eventually should be reviewed but not yet so) no review.
-rw-r--r--src/compiler/scala/reflect/api/Trees.scala161
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala19
2 files changed, 133 insertions, 47 deletions
diff --git a/src/compiler/scala/reflect/api/Trees.scala b/src/compiler/scala/reflect/api/Trees.scala
index 2addd401c9..0dbc09c46e 100644
--- a/src/compiler/scala/reflect/api/Trees.scala
+++ b/src/compiler/scala/reflect/api/Trees.scala
@@ -17,6 +17,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
type Modifiers <: AbsModifiers
+ /** Hook to define what toString means on a tree
+ */
+ def show(tree: Tree): String
+
abstract class AbsModifiers {
def hasModifier(mod: Modifier.Value): Boolean
def allModifiers: Set[Modifier.Value]
@@ -29,9 +33,49 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
privateWithin: Name = EmptyTypeName,
annotations: List[Tree] = List()): Modifiers
- // ------ tree base classes --------------------------------------------------
-
- /** The base class for all trees */
+ /** Tree is the basis for scala's abstract syntax. The nodes are
+ * implemented as case classes, and the parameters which initialize
+ * a given tree are immutable: however Trees have several mutable
+ * fields which are manipulated in the course of typechecking,
+ * including pos, symbol, and tpe.
+ *
+ * Newly instantiated trees have tpe set to null (though it
+ * may be set immediately thereafter depending on how it is
+ * constructed.) When a tree is passed to the typer, typically via
+ * `typer.typed(tree)`, under normal circumstances the tpe must be
+ * null or the typer will ignore it. Furthermore, the typer is not
+ * required to return the same tree it was passed.
+ *
+ * Trees can be easily traversed with e.g. foreach on the root node;
+ * for a more nuanced traversal, subclass Traverser. Transformations
+ * can be considerably trickier: see the numerous subclasses of
+ * Transformer found around the compiler.
+ *
+ * Copying Trees should be done with care depending on whether
+ * it need be done lazily or strictly (see LazyTreeCopier and
+ * StrictTreeCopier) and on whether the contents of the mutable
+ * fields should be copied. The tree copiers will copy the mutable
+ * attributes to the new tree; calling Tree#duplicate will copy
+ * symbol and tpe, but all the positions will be focused.
+ *
+ * Trees can be coarsely divided into four mutually exclusive categories:
+ *
+ * - TermTrees, representing terms
+ * - TypTrees, representing types. Note that is `TypTree`, not `TypeTree`.
+ * - SymTrees, which may represent types or terms.
+ * - Other Trees, which have none of those as parents.
+ *
+ * SymTrees include important nodes Ident and Select, which are
+ * used as both terms and types; they are distinguishable based on
+ * whether the Name is a TermName or TypeName. The correct way for
+ * to test for a type or a term (on any Tree) are the isTerm/isType
+ * methods on Tree.
+ *
+ * "Others" are mostly syntactic or short-lived constructs. Examples
+ * include CaseDef, which wraps individual match cases: they are
+ * neither terms nor types, nor do they carry a symbol. Another
+ * example is Parens, which is eliminated during parsing.
+ */
abstract class Tree extends Product {
val id = nodeCount
nodeCount += 1
@@ -56,6 +100,21 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
*/
def defineType(tp: Type): this.type = setType(tp)
+ /** Note that symbol is fixed as null at this level. In SymTrees,
+ * it is overridden and implemented with a var, initialized to NoSymbol.
+ *
+ * Trees which are not SymTrees but which carry symbols do so by
+ * overriding `def symbol` to forward it elsewhere. Examples:
+ *
+ * Super(qual, _) // has qual's symbol
+ * Apply(fun, args) // has fun's symbol
+ * TypeApply(fun, args) // has fun's symbol
+ * AppliedTypeTree(tpt, args) // has tpt's symbol
+ * TypeTree(tpe) // has tpe's typeSymbol, if tpe != null
+ *
+ * Attempting to set the symbol of a Tree which does not support
+ * it will induce an exception.
+ */
def symbol: Symbol = null
def symbol_=(sym: Symbol) { throw new UnsupportedOperationException("symbol_= inapplicable for " + this) }
def setSymbol(sym: Symbol): this.type = { symbol = sym; this }
@@ -66,6 +125,8 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
def hasSymbolWhich(f: Symbol => Boolean) = hasSymbol && f(symbol)
+ /** The canonical way to test if a Tree represents a term.
+ */
def isTerm: Boolean = this match {
case _: TermTree => true
case Bind(name, _) => name.isTermName
@@ -75,6 +136,8 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
case _ => false
}
+ /** The canonical way to test if a Tree represents a type.
+ */
def isType: Boolean = this match {
case _: TypTree => true
case Bind(name, _) => name.isTypeName
@@ -124,8 +187,8 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
(this.productIterator zip that.productIterator forall { case (x, y) => equals0(x, y) }) && compareOriginals()
})
- /** The direct child trees of this tree
- * EmptyTrees are always omitted. Lists are collapsed.
+ /** The direct child trees of this tree.
+ * EmptyTrees are always omitted. Lists are flattened.
*/
def children: List[Tree] = {
def subtrees(x: Any): List[Tree] = x match {
@@ -156,30 +219,38 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
}
- /** Hook to define what toString means on a tree
+ /** A tree for a term. Not all terms are TermTrees; use isTerm
+ * to reliably identify terms.
*/
- def show(tree: Tree): String
+ trait TermTree extends Tree
+ /** A tree for a type. Not all types are TypTrees; use isType
+ * to reliably identify types.
+ */
+ trait TypTree extends Tree
+
+ /** A tree with a mutable symbol field, initialized to NoSymbol.
+ */
trait SymTree extends Tree {
override def hasSymbol = true
override var symbol: Symbol = NoSymbol
}
+ /** A tree which references a symbol-carrying entity.
+ * References one, as opposed to defining one; definitions
+ * are in DefTrees.
+ */
trait RefTree extends SymTree {
def name: Name
}
+ /** A tree which defines a symbol-carrying entity.
+ */
abstract class DefTree extends SymTree {
def name: Name
override def isDef = true
}
- trait TermTree extends Tree
-
- /** A tree for a type. Note that not all type trees implement
- * this trait; in particular, Ident's are an exception. */
- trait TypTree extends Tree
-
// ----- tree node alternatives --------------------------------------
/** The empty tree */
@@ -190,6 +261,9 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
override def isEmpty = true
}
+ /** Common base class for all member definitions: types, classes,
+ * objects, packages, vals and vars, defs.
+ */
abstract class MemberDef extends DefTree {
def mods: Modifiers
def keyword: String = this match {
@@ -204,7 +278,7 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
// final def hasFlag(mask: Long): Boolean = mods hasFlag mask
}
- /** Package clause
+ /** A packaging, such as `package pid { stats }`
*/
case class PackageDef(pid: RefTree, stats: List[Tree])
extends MemberDef {
@@ -212,59 +286,63 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
def mods = Modifiers()
}
+ /** A common base class for class and object definitions.
+ */
abstract class ImplDef extends MemberDef {
def impl: Template
}
- /** Class definition */
+ /** A class definition.
+ */
case class ClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], impl: Template)
extends ImplDef
- /** Singleton object definition
+ /** An object definition, e.g. `object Foo`. Internally, objects are
+ * quite frequently called modules to reduce ambiguity.
*/
case class ModuleDef(mods: Modifiers, name: TermName, impl: Template)
extends ImplDef
+ /** A common base class for ValDefs and DefDefs.
+ */
abstract class ValOrDefDef extends MemberDef {
def name: TermName
def tpt: Tree
def rhs: Tree
}
- /** Value definition
+ /** A value definition (this includes vars as well, which differ from
+ * vals only in having the MUTABLE flag set in their Modifiers.)
*/
case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef
- /** Method definition
+ /** A method definition.
*/
case class DefDef(mods: Modifiers, name: TermName, tparams: List[TypeDef],
vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) extends ValOrDefDef
- /** Abstract type, type parameter, or type alias */
+ /** An abstract type, a type parameter, or a type alias.
+ */
case class TypeDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree)
extends MemberDef
- /** <p>
- * Labelled expression - the symbols in the array (must be Idents!)
- * are those the label takes as argument
- * </p>
- * <p>
- * The symbol that is given to the labeldef should have a MethodType
- * (as if it were a nested function)
- * </p>
- * <p>
- * Jumps are apply nodes attributed with label symbol, the arguments
- * will get assigned to the idents.
- * </p>
- * <p>
- * Note: on 2005-06-09 Martin, Iuli, Burak agreed to have forward
- * jumps within a Block.
- * </p>
+ /** A labelled expression. Not expressible in language syntax, but
+ * generated by the compiler to simulate while/do-while loops, and
+ * also by the pattern matcher.
+ *
+ * The label acts much like a nested function, where `params` represents
+ * the incoming parameters. The symbol given to the LabelDef should have
+ * a MethodType, as if it were a nested function.
+ *
+ * Jumps are apply nodes attributed with a label's symbol. The
+ * arguments from the apply node will be passed to the label and
+ * assigned to the Idents.
+ *
+ * Forward jumps within a block are allowed.
*/
case class LabelDef(name: TermName, params: List[Ident], rhs: Tree)
extends DefTree with TermTree
-
/** Import selector
*
* Representation of an imported name its optional rename and their optional positions
@@ -393,14 +471,19 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
case class Typed(expr: Tree, tpt: Tree)
extends TermTree
- // Martin to Sean: Should GenericApply/TypeApply/Apply not be SymTree's? After all,
- // ApplyDynamic is a SymTree.
+ /** Common base class for Apply and TypeApply. This could in principle
+ * be a SymTree, but whether or not a Tree is a SymTree isn't used
+ * to settle any interesting questions, and it would add a useless
+ * field to all the instances (useless, since GenericApply forwards to
+ * the underlying fun.)
+ */
abstract class GenericApply extends TermTree {
val fun: Tree
val args: List[Tree]
}
- /** Type application */
+ /** Explicit type application.
+ */
case class TypeApply(fun: Tree, args: List[Tree])
extends GenericApply {
override def symbol: Symbol = fun.symbol
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 21401d39dd..d4986e3dc1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1785,18 +1785,21 @@ trait Infer {
tree setSymbol resSym setType resTpe
}
- case class AccessError(tree: Tree, sym: Symbol, pre: Type, explanation: String) extends Tree {
- override def pos = tree.pos
- override def hasSymbol = tree.hasSymbol
- override def symbol = tree.symbol
- override def symbol_=(x: Symbol) = tree.symbol = x
+ abstract class TreeForwarder(forwardTo: Tree) extends Tree {
+ override def pos = forwardTo.pos
+ override def hasSymbol = forwardTo.hasSymbol
+ override def symbol = forwardTo.symbol
+ override def symbol_=(x: Symbol) = forwardTo.symbol = x
+ }
+
+ case class AccessError(tree: Tree, sym: Symbol, pre: Type, explanation: String) extends TreeForwarder(tree) {
setError(this)
+ // @PP: It is improbable this logic shouldn't be in use elsewhere as well.
+ private def location = if (sym.isClassConstructor) context.enclClass.owner else pre.widen
def emit(): Tree = {
val realsym = underlying(sym)
- errorTree(tree, realsym + realsym.locationString + " cannot be accessed in " +
- (if (sym.isClassConstructor) context.enclClass.owner else pre.widen) +
- explanation)
+ errorTree(tree, realsym.fullLocationString + " cannot be accessed in " + location + explanation)
}
}
}