diff options
Diffstat (limited to 'src')
24 files changed, 441 insertions, 313 deletions
diff --git a/src/dotty/tools/dotc/ast/Positioned.scala b/src/dotty/tools/dotc/ast/Positioned.scala index 8d364d439..bb6817603 100644 --- a/src/dotty/tools/dotc/ast/Positioned.scala +++ b/src/dotty/tools/dotc/ast/Positioned.scala @@ -24,7 +24,7 @@ abstract class Positioned extends DotClass with Product { * positions in children. */ protected def setPos(pos: Position): Unit = { - curPos = pos + setPosUnchecked(pos) if (pos.exists) setChildPositions(pos.toSynthetic) } @@ -107,7 +107,7 @@ abstract class Positioned extends DotClass with Product { /** The initial, synthetic position. This is usually the union of all positioned children's positions. */ - protected def initialPos: Position = { + def initialPos: Position = { var n = productArity var pos = NoPosition while (n > 0) { @@ -139,7 +139,7 @@ abstract class Positioned extends DotClass with Product { (this.pos contains that.pos) && { var n = productArity var found = false - while (n > 0 && !found) { + while (!found && n > 0) { n -= 1 found = isParent(productElement(n)) } diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 7911840c6..8b2915174 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -6,6 +6,7 @@ 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._ @@ -289,13 +290,14 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] 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: tpd.Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { + private def statPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { case EmptyTree | TypeDef(_, _) | Import(_, _) @@ -319,7 +321,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * 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: tpd.Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { + private def exprPurity(tree: Tree)(implicit ctx: Context): PurityLevel = unsplice(tree) match { case EmptyTree | This(_) | Super(_, _) @@ -358,8 +360,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => private def minOf(l0: PurityLevel, ls: List[PurityLevel]) = (l0 /: ls)(_ min _) - def isPureExpr(tree: tpd.Tree)(implicit ctx: Context) = exprPurity(tree) == Pure - def isIdempotentExpr(tree: tpd.Tree)(implicit ctx: Context) = exprPurity(tree) >= Idempotent + 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 @@ -369,17 +371,62 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable * flags set. */ - private def refPurity(tree: tpd.Tree)(implicit ctx: Context): PurityLevel = + 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: tpd.Tree)(implicit ctx: Context) = + def isPureRef(tree: Tree)(implicit ctx: Context) = refPurity(tree) == Pure - def isIdempotentRef(tree: tpd.Tree)(implicit ctx: Context) = + 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 = { @@ -394,7 +441,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => /** Is tree a reference to a mutable variable, or to a potential getter * that has a setter in the same class? */ - def isVariableOrGetter(tree: tpd.Tree)(implicit ctx: Context) = { + def isVariableOrGetter(tree: Tree)(implicit ctx: Context) = { def sym = tree.symbol def isVar = sym is Mutable def isGetter = @@ -419,8 +466,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ - def stripCast(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = { - def isCast(sel: tpd.Tree) = sel.symbol == defn.Any_asInstanceOf + 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) @@ -456,7 +503,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } /** If tree is a closure, its body, otherwise tree itself */ - def closureBody(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match { case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs case _ => tree } diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 2c02e7d1e..9108a4d22 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -60,15 +60,19 @@ object Trees { 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 */ - val uniqueId = { - nextId += 1 - //assert(nextId != 214, this) - nextId - } + private var myUniqueId: Int = nxId + + def uniqueId = myUniqueId /** The type constructor at the root of the tree */ type ThisTree[T >: Untyped] <: Tree[T] @@ -188,6 +192,12 @@ object Trees { 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 { @@ -502,7 +512,9 @@ object Trees { /** A tree representing inlined code. * - * @param call The original call that was inlined + * @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. * @@ -520,13 +532,12 @@ object Trees { } /** A type tree that represents an existing or inferred type */ - case class TypeTree[-T >: Untyped] private[ast] (original: Tree[T]) + case class TypeTree[-T >: Untyped] () extends DenotingTree[T] with TypTree[T] { type ThisTree[-T >: Untyped] = TypeTree[T] - override def initialPos = NoPosition - override def isEmpty = !hasType && original.isEmpty + override def isEmpty = !hasType override def toString = - s"TypeTree${if (hasType) s"[$typeOpt]" else s"($original)"}" + s"TypeTree${if (hasType) s"[$typeOpt]" else ""}" } /** ref.type */ @@ -960,10 +971,6 @@ object Trees { 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 TypeTree(tree: Tree)(original: Tree): TypeTree = tree match { - case tree: TypeTree if original eq tree.original => tree - case _ => finalize(tree, untpd.TypeTree(original)) - } def SingletonTypeTree(tree: Tree)(ref: Tree): SingletonTypeTree = tree match { case tree: SingletonTypeTree if ref eq tree.ref => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)) @@ -1106,7 +1113,7 @@ object Trees { cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) case Inlined(call, bindings, expansion) => cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)) - case TypeTree(original) => + case TypeTree() => tree case SingletonTypeTree(ref) => cpy.SingletonTypeTree(tree)(transform(ref)) @@ -1210,7 +1217,7 @@ object Trees { this(this(x, elems), elemtpt) case Inlined(call, bindings, expansion) => this(this(x, bindings), expansion) - case TypeTree(original) => + case TypeTree() => x case SingletonTypeTree(ref) => this(x, ref) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index d8db3306c..273fc8d0a 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -121,11 +121,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(implicit ctx: Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def TypeTree(original: Tree)(implicit ctx: Context): TypeTree = - TypeTree(original.tpe, original) - - def TypeTree(tp: Type, original: Tree = EmptyTree)(implicit ctx: Context): TypeTree = - untpd.TypeTree(original).withType(tp) + 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) diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 6513dfdc3..ac3beaff4 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -186,7 +186,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { /** 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(EmptyTree) { + abstract class DerivedTypeTree extends TypeTree { private var myWatched: Tree = EmptyTree @@ -247,8 +247,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { 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(original: Tree): TypeTree = new TypeTree(original) - def TypeTree() = new TypeTree(EmptyTree) + 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) diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 63bb00a71..4b67bc188 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -13,15 +13,32 @@ import collection.mutable import TastyBuffer._ import util.Positions._ -class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr]) { +class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr]) { val buf = new TastyBuffer(5000) pickler.newSection("Positions", buf) import buf._ import ast.tpd._ - def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean) = { + private val remainingAddrs = new java.util.IdentityHashMap[Tree, Iterator[Addr]] + + def nextTreeAddr(tree: Tree): Option[Addr] = remainingAddrs.get(tree) match { + case null => + addrsOfTree(tree) match { + case Nil => + None + case addr :: Nil => + Some(addr) + case addrs => + remainingAddrs.put(tree, addrs.iterator) + nextTreeAddr(tree) + } + case it: Iterator[_] => + if (it.hasNext) Some(it.next) else None + } + + def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean, hasPoint: Boolean) = { def toInt(b: Boolean) = if (b) 1 else 0 - (addrDelta << 2) | (toInt(hasStartDelta) << 1) | toInt(hasEndDelta) + (addrDelta << 3) | (toInt(hasStartDelta) << 2) | (toInt(hasEndDelta) << 1) | toInt(hasPoint) } def picklePositions(roots: List[Tree])(implicit ctx: Context) = { @@ -31,29 +48,46 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr val addrDelta = index - lastIndex val startDelta = pos.start - lastPos.start val endDelta = pos.end - lastPos.end - buf.writeInt(header(addrDelta, startDelta != 0, endDelta != 0)) + buf.writeInt(header(addrDelta, startDelta != 0, endDelta != 0, !pos.isSynthetic)) if (startDelta != 0) buf.writeInt(startDelta) if (endDelta != 0) buf.writeInt(endDelta) + if (!pos.isSynthetic) buf.writeInt(pos.pointDelta) lastIndex = index lastPos = pos } - def traverse(x: Any, parentPos: Position): Unit = x match { + + /** True if x's position cannot be reconstructed automatically from its initialPos + */ + def alwaysNeedsPos(x: Positioned) = x match { + case _: WithLazyField[_] // initialPos is inaccurate for trees with lazy field + | _: Trees.PackageDef[_] => true // package defs might be split into several Tasty files + case x: Trees.Tree[_] => x.isType // types are unpickled as TypeTrees, so child positions are not available + case _ => false + } + + def traverse(x: Any): Unit = x match { case x: Tree @unchecked => - if (x.pos.exists && x.pos.toSynthetic != parentPos.toSynthetic) { - addrOfTree(x) match { - case Some(addr) => pickleDeltas(addr.index, x.pos) + val pos = if (x.isInstanceOf[MemberDef]) x.pos else x.pos.toSynthetic + if (pos.exists && (pos != x.initialPos.toSynthetic || alwaysNeedsPos(x))) { + nextTreeAddr(x) match { + case Some(addr) => + //println(i"pickling $x with $pos at $addr") + pickleDeltas(addr.index, pos) case _ => + //println(i"no address for $x") } } + //else if (x.pos.exists) println(i"skipping $x") x match { - case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos) + case x: MemberDef @unchecked => + for (ann <- x.symbol.annotations) traverse(ann.tree) case _ => } - traverse(x.productIterator, x.pos) + traverse(x.productIterator) case xs: TraversableOnce[_] => - xs.foreach(traverse(_, parentPos)) + xs.foreach(traverse) case _ => } - traverse(roots, NoPosition) + traverse(roots) } } diff --git a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala index c29aeba70..cbe213d89 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala @@ -19,14 +19,17 @@ class PositionUnpickler(reader: TastyReader) { var curEnd = 0 while (!isAtEnd) { val header = readInt() - val addrDelta = header >> 2 - val hasStart = (header & 2) != 0 - val hasEnd = (header & 1) != 0 + val addrDelta = header >> 3 + val hasStart = (header & 4) != 0 + val hasEnd = (header & 2) != 0 + val hasPoint = (header & 1) != 0 curIndex += addrDelta assert(curIndex >= 0) if (hasStart) curStart += readInt() if (hasEnd) curEnd += readInt() - positions(Addr(curIndex)) = Position(curStart, curEnd) + positions(Addr(curIndex)) = + if (hasPoint) Position(curStart, curEnd, curStart + readInt()) + else Position(curStart, curEnd) } positions } diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 8e8d58b47..f9743d9d2 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -57,7 +57,8 @@ Standard-Section: "ASTs" TopLevelStat* TYPEDEF Length NameRef (Type | Template) Modifier* IMPORT Length qual_Term Selector* Selector = IMPORTED name_NameRef - RENAMED Length from_NameRef to_NameRef + RENAMED to_NameRef + // Imports are for scala.meta, they are not used in the backend TypeParam = TYPEPARAM Length NameRef Type Modifier* @@ -78,6 +79,7 @@ Standard-Section: "ASTs" TopLevelStat* NAMEDARG Length paramName_NameRef arg_Term ASSIGN Length lhs_Term rhs_Term BLOCK Length expr_Term Stat* + INLINED Length call_Term expr_Term Stat* LAMBDA Length meth_Term target_Type IF Length cond_Term then_Term else_Term MATCH Length sel_Term CaseDef* @@ -137,12 +139,11 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef bounds_Type // for type-variables defined in a type pattern BYNAMEtype underlying_Type - LAMBDAtype Length result_Type NamesTypes // variance encoded in front of name: +/-/= - POLYtype Length result_Type NamesTypes // needed for refinements + POLYtype Length result_Type NamesTypes // variance encoded in front of name: +/-/= METHODtype Length result_Type NamesTypes // needed for refinements PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements SHARED type_ASTRef - NamesTypes = ParamType* + NamesTypes = NameType* NameType = paramName_NameRef typeOrBounds_ASTRef Modifier = PRIVATE @@ -264,6 +265,7 @@ object TastyFormat { final val DOUBLEconst = 76 final val STRINGconst = 77 final val IMPORTED = 78 + final val RENAMED = 79 final val THIS = 96 final val CLASSconst = 97 @@ -291,11 +293,10 @@ object TastyFormat { final val TYPEPARAM = 133 final val PARAMS = 134 final val PARAM = 136 - final val RENAMED = 138 + final val APPLY = 139 final val TYPEAPPLY = 140 - final val TYPED = 143 final val NAMEDARG = 144 final val ASSIGN = 145 @@ -305,6 +306,7 @@ object TastyFormat { final val MATCH = 149 final val RETURN = 150 final val TRY = 151 + final val INLINED = 152 final val REPEATED = 153 final val BIND = 154 final val ALTERNATIVE = 155 @@ -322,9 +324,8 @@ object TastyFormat { final val ORtype = 172 final val METHODtype = 174 final val POLYtype = 175 - final val LAMBDAtype = 176 - final val PARAMtype = 177 - final val ANNOTATION = 178 + final val PARAMtype = 176 + final val ANNOTATION = 177 final val firstSimpleTreeTag = UNITconst final val firstNatTreeTag = SHARED @@ -455,6 +456,7 @@ object TastyFormat { case LAMBDA => "LAMBDA" case MATCH => "MATCH" case RETURN => "RETURN" + case INLINED => "INLINED" case TRY => "TRY" case REPEATED => "REPEATED" case BIND => "BIND" @@ -485,7 +487,7 @@ object TastyFormat { case PROTECTEDqualified => "PROTECTEDqualified" } - /** @return If non-negative, the number of leading references of a length/trees entry. + /** @return If non-negative, the number of leading references (represented as nats) of a length/trees entry. * If negative, minus the number of leading non-reference trees. */ def numRefs(tag: Int) = tag match { diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala index 98b0dc7c6..f847dda68 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -57,9 +57,11 @@ class TastyPickler { /** * Addresses in TASTY file of trees, stored by pickling. * Note that trees are checked for reference equality, - * so one can reliably use this function only directly after `pickler` + * so one can reliably use this function only directly after `pickler`. + * Note that a tree can have several addresses, if it is shared, + * i.e. accessible from different paths. Any such sharing is undone by pickling. */ - var addrOfTree: tpd.Tree => Option[Addr] = (_ => None) + var addrsOfTree: tpd.Tree => List[Addr] = (_ => Nil) /** * Addresses in TASTY file of symbols, stored by pickling. diff --git a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala index d741c42c3..f2681ecde 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala @@ -6,7 +6,7 @@ package tasty import util.Util.{bestFit, dble} import TastyBuffer.{Addr, AddrWidth} import config.Printers.pickling -import ast.tpd.Tree +import ast.untpd.Tree class TreeBuffer extends TastyBuffer(50000) { @@ -17,11 +17,26 @@ class TreeBuffer extends TastyBuffer(50000) { private var delta: Array[Int] = _ private var numOffsets = 0 - private[tasty] val pickledTrees = new java.util.IdentityHashMap[Tree, Any] // Value type is really Addr, but that's not compatible with null + private type TreeAddrs = Any // really: Addr | List[Addr] - def addrOfTree(tree: Tree): Option[Addr] = pickledTrees.get(tree) match { - case null => None - case n => Some(n.asInstanceOf[Addr]) + /** A map from trees to the address(es) at which a tree is pickled. There may be several + * such addresses if the tree is shared. To keep the map compact, the value type is a + * disjunction of a single address (which is the common case) and a list of addresses. + */ + private val treeAddrs = new java.util.IdentityHashMap[Tree, TreeAddrs] + + def registerTreeAddr(tree: Tree) = + treeAddrs.put(tree, + treeAddrs.get(tree) match { + case null => currentAddr + case x: Addr => x :: currentAddr :: Nil + case xs: List[_] => xs :+ currentAddr + }) + + def addrsOfTree(tree: Tree): List[Addr] = treeAddrs.get(tree) match { + case null => Nil + case addr: Addr => addr :: Nil + case addrs: List[Addr] => addrs } private def offset(i: Int): Addr = Addr(offsets(i)) @@ -147,11 +162,14 @@ class TreeBuffer extends TastyBuffer(50000) { wasted } - def adjustPickledTrees(): Unit = { - val it = pickledTrees.keySet.iterator + def adjustTreeAddrs(): Unit = { + val it = treeAddrs.keySet.iterator while (it.hasNext) { val tree = it.next - pickledTrees.put(tree, adjusted(pickledTrees.get(tree).asInstanceOf[Addr])) + treeAddrs.get(tree) match { + case addr: Addr => treeAddrs.put(tree, adjusted(addr)) + case addrs: List[Addr] => treeAddrs.put(tree, addrs.map(adjusted)) + } } } @@ -172,7 +190,7 @@ class TreeBuffer extends TastyBuffer(50000) { pickling.println(s"adjusting deltas, saved = $saved") } while (saved > 0 && length / saved < 100) adjustOffsets() - adjustPickledTrees() + adjustTreeAddrs() val wasted = compress() pickling.println(s"original length: $origLength, compressed to: $length, wasted: $wasted") // DEBUG, for now. } diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8889e8a5c..9dfb78798 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -4,6 +4,7 @@ package core package tasty import ast.Trees._ +import ast.untpd import TastyFormat._ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ import collection.mutable @@ -48,6 +49,10 @@ class TreePickler(pickler: TastyPickler) { case None => } } + + def rhs(tdef: TypeDef)(implicit ctx: Context) = + if (tdef.symbol.isClass) tdef.rhs + else TypeTree(tdef.symbol.info).withPos(tdef.rhs.pos) private def pickleName(name: Name): Unit = writeNat(nameIndex(name).index) private def pickleName(name: TastyName): Unit = writeNat(nameIndex(name).index) @@ -152,6 +157,11 @@ class TreePickler(pickler: TastyPickler) { throw ex } + def pickleTypeWithPos(tpe: Type, tree: Tree)(implicit ctx: Context): Unit = { + registerTreeAddr(tree) + pickleType(tpe) + } + private def pickleNewType(tpe: Type, richTypes: Boolean)(implicit ctx: Context): Unit = try { tpe match { case AppliedType(tycon, args) => writeByte(APPLIEDtype) @@ -252,7 +262,7 @@ class TreePickler(pickler: TastyPickler) { writeByte(BYNAMEtype) pickleType(tpe.underlying) case tpe: PolyType => - writeByte(LAMBDAtype) + writeByte(POLYtype) val paramNames = tpe.typeParams.map(tparam => varianceToPrefix(tparam.paramVariance) +: tparam.paramName) pickleMethodic(tpe.resultType, paramNames, tpe.paramBounds) @@ -299,7 +309,8 @@ class TreePickler(pickler: TastyPickler) { pickled } - def pickleTpt(tpt: Tree)(implicit ctx: Context): Unit = pickleType(tpt.tpe) // TODO correlate with original when generating positions + def pickleTpt(tpt: Tree)(implicit ctx: Context): Unit = + pickleTypeWithPos(tpt.tpe, tpt) // TODO correlate with original when generating positions def pickleTreeUnlessEmpty(tree: Tree)(implicit ctx: Context): Unit = if (!tree.isEmpty) pickleTree(tree) @@ -312,18 +323,21 @@ class TreePickler(pickler: TastyPickler) { pickleName(sym) pickleParams tpt match { - case tpt: TypeTree => pickleTpt(tpt) - case _ => pickleTree(tpt) + case templ: Template => pickleTree(tpt) + case _ if tpt.isType => pickleTpt(tpt) } pickleTreeUnlessEmpty(rhs) pickleModifiers(sym) } } - def pickleParam(tree: Tree)(implicit ctx: Context): Unit = tree match { - case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt) - case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs) - case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, tree.rhs) + def pickleParam(tree: Tree)(implicit ctx: Context): Unit = { + registerTreeAddr(tree) + tree match { + case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt) + case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs) + case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, rhs(tree)) + } } def pickleParams(trees: List[Tree])(implicit ctx: Context): Unit = { @@ -337,8 +351,10 @@ class TreePickler(pickler: TastyPickler) { } def pickleTree(tree: Tree)(implicit ctx: Context): Unit = try { - pickledTrees.put(tree, currentAddr) + registerTreeAddr(tree) tree match { + case tree if tree.isType => + pickleTpt(tree) case Ident(name) => tree.tpe match { case tp: TermRef => pickleType(tp) @@ -428,17 +444,10 @@ class TreePickler(pickler: TastyPickler) { case SeqLiteral(elems, elemtpt) => writeByte(REPEATED) withLength { pickleTree(elemtpt); elems.foreach(pickleTree) } - case tree: Inlined => - // Why drop Inlined info when pickling? - // Since we never inline inside an inlined method, we know that - // any code that continas an Inlined tree is not inlined itself. - // So position information for inline expansion is no longer needed. - // The only reason to keep the inline info around would be to have fine-grained - // position information in the linker. We should come back to this - // point once we know more what we would do with such information. - pickleTree(Inliner.dropInlined(tree)) - case TypeTree(original) => - pickleTpt(tree) + case Inlined(call, bindings, expansion) => + writeByte(INLINED) + bindings.foreach(preRegister) + withLength { pickleTree(call); pickleTree(expansion); bindings.foreach(pickleTree) } case Bind(name, body) => registerDef(tree.symbol) writeByte(BIND) @@ -469,7 +478,7 @@ class TreePickler(pickler: TastyPickler) { } pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) case tree: TypeDef => - pickleDef(TYPEDEF, tree.symbol, tree.rhs) + pickleDef(TYPEDEF, tree.symbol, rhs(tree)) case tree: Template => registerDef(tree.symbol) writeByte(TEMPLATE) @@ -486,6 +495,7 @@ class TreePickler(pickler: TastyPickler) { if ((selfInfo ne NoType) || !tree.self.isEmpty) { writeByte(SELFDEF) pickleName(tree.self.name) + if (!tree.self.isEmpty) registerTreeAddr(tree.self.tpt) pickleType { cinfo.selfInfo match { case sym: Symbol => sym.info @@ -500,12 +510,11 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleTree(expr) selectors foreach { - case Thicket(Ident(from) :: Ident(to) :: Nil) => - writeByte(RENAMED) - withLength { pickleName(from); pickleName(to) } - case Ident(name) => - writeByte(IMPORTED) - pickleName(name) + case Thicket((from @ Ident(_)) :: (to @ Ident(_)) :: Nil) => + pickleSelector(IMPORTED, from) + pickleSelector(RENAMED, to) + case id @ Ident(_) => + pickleSelector(IMPORTED, id) } } case PackageDef(pid, stats) => @@ -518,6 +527,12 @@ class TreePickler(pickler: TastyPickler) { throw ex } + def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = { + registerTreeAddr(id) + writeByte(tag) + pickleName(id.name) + } + def qualifiedName(sym: Symbol)(implicit ctx: Context): TastyName = if (sym.isRoot || sym.owner.isRoot) TastyName.Simple(sym.name.toTermName) else TastyName.Qualified(nameIndex(qualifiedName(sym.owner)), nameIndex(sym.name)) diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index f67159808..a0d788955 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -266,7 +266,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType()) registerSym(start, sym) TypeRef.withFixedSym(NoPrefix, sym.name, sym) - case LAMBDAtype => + case POLYtype => val (rawNames, paramReader) = readNamesSkipParams val (variances, paramNames) = rawNames .map(name => (prefixToVariance(name.head), name.tail.toTypeName)).unzip @@ -275,13 +275,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle pt => readType()) goto(end) result - case POLYtype => - val (names, paramReader) = readNamesSkipParams - val result = PolyType(names.map(_.toTypeName))( - pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)), - pt => readType()) - goto(end) - result case METHODtype => val (names, paramReader) = readNamesSkipParams val result = MethodType(names.map(_.toTermName), paramReader.readParamTypes[Type](end))( @@ -383,12 +376,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readTypeRef(): Type = typeAtAddr(readAddr()) - def readPath()(implicit ctx: Context): Type = { - val tp = readType() - assert(tp.isInstanceOf[SingletonType]) - tp - } - def readTermRef()(implicit ctx: Context): TermRef = readType().asInstanceOf[TermRef] @@ -655,12 +642,18 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def localCtx = localContext(sym) + def ValDef(tpt: Tree) = + ta.assignType(untpd.ValDef(sym.name.asTermName, tpt, readRhs(localCtx)), sym) + def DefDef(tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree) = ta.assignType( untpd.DefDef( sym.name.asTermName, tparams, vparamss, tpt, readRhs(localCtx)), sym) + def TypeDef(rhs: Tree) = + ta.assignType(untpd.TypeDef(sym.name.asTypeName, rhs), sym) + def ta = ctx.typeAssigner val name = readName() @@ -682,8 +675,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle } DefDef(tparams, vparamss, tpt) case VALDEF => - sym.info = readType() - ValDef(sym.asTerm, readRhs(localCtx)) + val tpt = readTpt() + sym.info = tpt.tpe + ValDef(tpt) case TYPEDEF | TYPEPARAM => if (sym.isClass) { val companion = sym.scalacLinkedClass @@ -699,22 +693,23 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle if (sym is Flags.ModuleClass) sym.registerCompanionMethod(nme.COMPANION_CLASS_METHOD, companion) else sym.registerCompanionMethod(nme.COMPANION_MODULE_METHOD, companion) } - ta.assignType(untpd.TypeDef(sym.name.asTypeName, readTemplate(localCtx)), sym) + TypeDef(readTemplate(localCtx)) } else { - sym.info = readType() - TypeDef(sym.asType) + val rhs = readTpt() + sym.info = rhs.tpe + TypeDef(rhs) } case PARAM => - val info = readType() + val tpt = readTpt() if (noRhs(end)) { - sym.info = info - ValDef(sym.asTerm) + sym.info = tpt.tpe + ValDef(tpt) } else { sym.setFlag(Method) - sym.info = ExprType(info) + sym.info = ExprType(tpt.tpe) pickling.println(i"reading param alias $name -> $currentAddr") - DefDef(Nil, Nil, TypeTree(info)) + DefDef(Nil, Nil, tpt) } } val mods = @@ -816,21 +811,28 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle } def readImport()(implicit ctx: Context): Tree = { + val start = currentAddr readByte() readEnd() val expr = readTerm() def readSelectors(): List[untpd.Tree] = nextByte match { - case RENAMED => - readByte() - readEnd() - untpd.Thicket(untpd.Ident(readName()), untpd.Ident(readName())) :: readSelectors() case IMPORTED => + val start = currentAddr readByte() - untpd.Ident(readName()) :: readSelectors() - case _ => - Nil + val from = setPos(start, untpd.Ident(readName())) + nextByte match { + case RENAMED => + val start2 = currentAddr + readByte() + val to = setPos(start2, untpd.Ident(readName())) + untpd.Thicket(from, to) :: readSelectors() + case _ => + from :: readSelectors() + } + case _ => + Nil } - Import(expr, readSelectors()) + setPos(start, Import(expr, readSelectors())) } def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = @@ -853,7 +855,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readPathTerm(): Tree = { goto(start) - readPath() match { + readType() match { case path: TermRef => ref(path) case path: ThisType => This(path.cls) case path: ConstantType => Literal(path.value) @@ -889,6 +891,15 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readLengthTerm(): Tree = { val end = readEnd() + def readBlock(mkTree: (List[Tree], Tree) => Tree): Tree = { + val exprReader = fork + skipTree() + val localCtx = ctx.fresh.setNewScope + val stats = readStats(ctx.owner, end)(localCtx) + val expr = exprReader.readTerm()(localCtx) + mkTree(stats, expr) + } + val result = (tag: @switch) match { case SUPER => @@ -921,12 +932,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle case ASSIGN => Assign(readTerm(), readTerm()) case BLOCK => - val exprReader = fork - skipTree() - val localCtx = ctx.fresh.setNewScope - val stats = readStats(ctx.owner, end)(localCtx) - val expr = exprReader.readTerm()(localCtx) - Block(stats, expr) + readBlock(Block) + case INLINED => + val call = setPos(currentAddr, TypeTree(readType())) + readBlock((defs, expr) => Inlined(call, defs.asInstanceOf[List[MemberDef]], expr)) case IF => If(readTerm(), readTerm(), readTerm()) case LAMBDA => @@ -1002,11 +1011,17 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle // ------ Setting positions ------------------------------------------------ /** Set position of `tree` at given `addr`. */ - def setPos[T <: Tree](addr: Addr, tree: T)(implicit ctx: Context): tree.type = + def setPos[T <: untpd.Tree](addr: Addr, tree: T)(implicit ctx: Context): tree.type = if (ctx.mode.is(Mode.ReadPositions)) { posUnpicklerOpt match { - case Some(posUnpickler) => tree.withPos(posUnpickler.posAt(addr)) - case _ => tree + case Some(posUnpickler) => + //println(i"setPos $tree / ${tree.getClass} at $addr to ${posUnpickler.posAt(addr)}") + val pos = posUnpickler.posAt(addr) + if (pos.exists) tree.setPosUnchecked(pos) + tree + case _ => + //println(i"no pos $tree") + tree } } else tree diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 6315cfabc..7aaf2e190 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -351,12 +351,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case SeqLiteral(elems, elemtpt) => "[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]" case tree @ Inlined(call, bindings, body) => - if (homogenizedView) toTextCore(Inliner.dropInlined(tree.asInstanceOf[tpd.Inlined])) - else "/* inlined from " ~ toText(call) ~ "*/ " ~ blockText(bindings :+ body) + (("/* inlined from " ~ toText(call) ~ "*/ ") provided !homogenizedView) ~ + blockText(bindings :+ body) case tpt: untpd.DerivedTypeTree => "<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">" - case TypeTree(orig) => - if (tree.hasType) toText(tree.typeOpt) else toText(orig) + case TypeTree() => + toText(tree.typeOpt) case SingletonTypeTree(ref) => toTextLocal(ref) ~ ".type" case AndTypeTree(l, r) => @@ -426,7 +426,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case _ => toTextGlobal(sel) } val selectorsText: Text = selectors match { - case Ident(name) :: Nil => toText(name) + case id :: Nil => toText(id) case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}" } "import " ~ toTextLocal(expr) ~ "." ~ selectorsText @@ -525,8 +525,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (tree.isType) txt = toText(tp) else if (!tree.isDef) txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close } - if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) - txt = txt ~ "@" ~ tree.pos.toString + else if (homogenizedView && tree.isType) + txt = toText(tree.typeOpt) + if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) { + val pos = + if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic + else tree.pos + val clsStr = "" // DEBUG: if (tree.isType) tree.getClass.toString else "" + txt = (txt ~ "@" ~ pos.toString ~ clsStr).close + } tree match { case Block(_, _) | Template(_, _, _, _) => txt case _ => txt.close diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index 74dc9b9d6..597146514 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -28,7 +28,9 @@ import StdNames._ * - ensures there are companion objects for all classes except module classes * - eliminates some kinds of trees: Imports, NamedArgs * - stubs out native methods - * - eliminate self tree in Template and self symbol in ClassInfo + * - eliminates self tree in Template and self symbol in ClassInfo + * - collapsess all type trees to trees of class TypeTree + * - converts idempotent expressions with constant types */ class FirstTransform extends MiniPhaseTransform with InfoTransformer with AnnotationTransformer { thisTransformer => import ast.tpd._ @@ -146,7 +148,7 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { cpy.Template(impl)(self = EmptyValDef) } - + override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = { if (ddef.symbol.hasAnnotation(defn.NativeAnnot)) { ddef.symbol.resetFlag(Deferred) @@ -162,9 +164,29 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { case tree: Import => EmptyTree case tree: NamedArg => transform(tree.arg) - case tree => tree + case tree => if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree } + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = + if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) + else constToLiteral(tree) + + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = + if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) + else constToLiteral(tree) + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) = + constToLiteral(tree) + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = + constToLiteral(tree) + + override def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo) = + constToLiteral(tree) + + override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo) = + constToLiteral(tree) + // invariants: all modules have companion objects // all types are TypeTrees // all this types are explicit diff --git a/src/dotty/tools/dotc/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala index 90e62b65c..fc70ac4f2 100644 --- a/src/dotty/tools/dotc/transform/Pickler.scala +++ b/src/dotty/tools/dotc/transform/Pickler.scala @@ -46,10 +46,10 @@ class Pickler extends Phase { val treePkl = pickler.treePkl treePkl.pickle(tree :: Nil) treePkl.compactify() - pickler.addrOfTree = treePkl.buf.addrOfTree + pickler.addrsOfTree = treePkl.buf.addrsOfTree pickler.addrOfSym = treePkl.addrOfSym if (tree.pos.exists) - new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil) + new PositionPickler(pickler, treePkl.buf.addrsOfTree).picklePositions(tree :: Nil) def rawBytes = // not needed right now, but useful to print raw format. pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map { @@ -66,7 +66,10 @@ class Pickler extends Phase { override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = { val result = super.runOn(units) if (ctx.settings.YtestPickler.value) - testUnpickler(units)(ctx.fresh.setPeriod(Period(ctx.runId + 1, FirstPhaseId))) + testUnpickler(units)( + ctx.fresh + .setPeriod(Period(ctx.runId + 1, FirstPhaseId)) + .addMode(Mode.ReadPositions)) result } @@ -81,7 +84,7 @@ class Pickler extends Phase { } pickling.println("************* entered toplevel ***********") for ((cls, unpickler) <- unpicklers) { - val unpickled = unpickler.body(ctx.addMode(Mode.ReadPositions)) + val unpickled = unpickler.body testSame(i"$unpickled%\n%", beforePickling(cls), cls) } } diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 12d48d98e..7e51635e5 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -41,6 +41,9 @@ import Symbols._, TypeUtils._ * * (10) Adds Child annotations to all sealed classes * + * (11) Minimizes `call` fields of `Inline` nodes to just point to the toplevel + * class from which code was inlined. + * * The reason for making this a macro transform is that some functions (in particular * super and protected accessors and instantiation checks) are naturally top-down and * don't lend themselves to the bottom-up approach of a mini phase. The other two functions @@ -73,48 +76,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // TODO fill in } - /** Check bounds of AppliedTypeTrees and TypeApplys. - * Replace type trees with TypeTree nodes. - * Replace constant expressions with Literal nodes. - * 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. - */ - private def normalizeTree(tree: Tree)(implicit ctx: Context): Tree = tree match { - case _: TypeTree | _: TypeApply => tree - case _ => - if (tree.isType) { - Checking.typeChecker.traverse(tree) - TypeTree(tree.tpe).withPos(tree.pos) - } - else tree.tpe.widenTermRefExpr match { - case ConstantType(value) if isIdempotentExpr(tree) => Literal(value) - case _ => tree - } - } - /** If the type of `tree` is a TermRefWithSignature with an underdefined * signature, narrow the type by re-computing the signature (which should * be fully-defined by now). @@ -159,7 +120,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) => transformSelect(cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name), targs) case _ => - superAcc.transformSelect(super.transform(tree), targs) + val tree1 = super.transform(tree) + constToLiteral(tree1) match { + case _: Literal => tree1 + case _ => superAcc.transformSelect(tree1, targs) + } } } @@ -199,14 +164,19 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } override def transform(tree: Tree)(implicit ctx: Context): Tree = - try normalizeTree(tree) match { - case tree: Ident => + try tree match { + case tree: Ident if !tree.isType => tree.tpe match { case tpe: ThisType => This(tpe.cls).withPos(tree.pos) case _ => paramFwd.adaptRef(fixSignature(tree)) } - case tree: Select => - transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil) + case tree @ Select(qual, name) => + if (name.isTypeName) { + Checking.checkRealizable(qual.tpe, qual.pos.focus) + super.transform(tree) + } + else + transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil) case tree: Super => if (ctx.owner.enclosingMethod.isInlineMethod) ctx.error(em"super not allowed in inline ${ctx.owner}", tree.pos) @@ -224,6 +194,19 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } case tree @ Assign(sel: Select, _) => superAcc.transformAssign(super.transform(tree)) + case Inlined(call, bindings, expansion) => + // Leave only a call trace consisting of + // - a reference to the top-level class from which the call was inlined, + // - the call's position + // in the call field of an Inlined node. + // The trace has enough info to completely reconstruct positions. + // The minimization is done for two reasons: + // 1. To save space (calls might contain large inline arguments, which would otherwise + // be duplicated + // 2. To enable correct pickling (calls can share symbols with the inlined code, which + // would trigger an assertion when pickling). + val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) + cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)) case tree: Template => val saved = parentNews parentNews ++= tree.parents.flatMap(newPart) @@ -240,26 +223,23 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tree: TypeDef => transformMemberDef(tree) val sym = tree.symbol - val tree1 = - if (sym.isClass) { - if (sym.owner.is(Package) && - ctx.compilationUnit.source.exists && - sym != defn.SourceFileAnnot) - sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path)) + if (sym.isClass) { + // Add SourceFile annotation to top-level classes + if (sym.owner.is(Package) && + ctx.compilationUnit.source.exists && + sym != defn.SourceFileAnnot) + sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path)) - if (!sym.isAnonymousClass) // ignore anonymous class - for (parent <- sym.asClass.classInfo.classParents) { - val pclazz = parent.classSymbol - if (pclazz.is(Sealed)) pclazz.addAnnotation(Annotation.makeChild(sym)) - } + // Add Child annotation to sealed parents unless current class is anonymous + if (!sym.isAnonymousClass) // ignore anonymous class + for (parent <- sym.asClass.classInfo.classParents) { + val pclazz = parent.classSymbol + if (pclazz.is(Sealed)) pclazz.addAnnotation(Annotation.makeChild(sym)) + } - tree - } - else { - Checking.typeChecker.traverse(tree.rhs) - cpy.TypeDef(tree)(rhs = TypeTree(tree.symbol.info)) - } - super.transform(tree1) + tree + } + super.transform(tree) case tree: MemberDef => transformMemberDef(tree) super.transform(tree) @@ -268,6 +248,12 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran super.transform(tree) case tree @ Annotated(annotated, annot) => cpy.Annotated(tree)(transform(annotated), transformAnnot(annot)) + case tree: AppliedTypeTree => + Checking.checkAppliedType(tree) + super.transform(tree) + case SingletonTypeTree(ref) => + Checking.checkRealizable(ref.tpe, ref.pos.focus) + super.transform(tree) case tree: TypeTree => tree.withType( tree.tpe match { diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index fde4db811..dc4454439 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -354,7 +354,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete t // todo: could improve to handle DefDef's with a label flag calls to which are in tail position case ValDef(_, _, _) | EmptyTree | Super(_, _) | This(_) | - Literal(_) | TypeTree(_) | TypeDef(_, _) => + Literal(_) | TypeTree() | TypeDef(_, _) => tree case Return(expr, from) => diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 45fa3d607..a1ccf0e63 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -1122,10 +1122,7 @@ object TreeTransforms { case tree: TypeTree => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur) if (mutatedInfo eq null) tree - else { - val original = transform(tree.original, mutatedInfo, cur) - goTypeTree(cpy.TypeTree(tree)(original), mutatedInfo.nx.nxTransTypeTree(cur)) - } + else goTypeTree(tree, mutatedInfo.nx.nxTransTypeTree(cur)) case tree: Alternative => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAlternative, info.nx.nxPrepAlternative, tree, cur) if (mutatedInfo eq null) tree diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 3ebae733f..dbfc89f6c 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -55,58 +55,45 @@ object Checking { def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = checkBounds(args, poly.paramBounds, _.substParams(poly, _)) - /** If type is a higher-kinded application with wildcard arguments, - * check that it or one of its supertypes can be reduced to a normal application. - * Unreducible applications correspond to general existentials, and we - * cannot handle those. + /** Check applied type trees for well-formedness. This means + * - all arguments are within their corresponding bounds + * - if type is a higher-kinded application with wildcard arguments, + * check that it or one of its supertypes can be reduced to a normal application. + * Unreducible applications correspond to general existentials, and we + * cannot handle those. */ - def checkWildcardHKApply(tp: Type, pos: Position)(implicit ctx: Context): Unit = tp match { - case tp @ HKApply(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) => - tycon match { - case tycon: PolyType => - ctx.errorOrMigrationWarning( - ex"unreducible application of higher-kinded type $tycon to wildcard arguments", - pos) - case _ => - checkWildcardHKApply(tp.superType, pos) - } - case _ => - } - - /** Traverse type tree, performing the following checks: - * 1. All arguments of applied type trees must conform to their bounds. - * 2. Prefixes of type selections and singleton types must be realizable. - */ - val typeChecker = new TreeTraverser { - def traverse(tree: Tree)(implicit ctx: Context) = { - tree match { - case AppliedTypeTree(tycon, args) => - // If `args` is a list of named arguments, return corresponding type parameters, - // otherwise return type parameters unchanged - val tparams = tycon.tpe.typeParams - def argNamed(tparam: TypeParamInfo) = args.find { - case NamedArg(name, _) => name == tparam.paramName - case _ => false - }.getOrElse(TypeTree(tparam.paramRef)) - val orderedArgs = if (hasNamedArg(args)) tparams.map(argNamed) else args - val bounds = tparams.map(_.paramBoundsAsSeenFrom(tycon.tpe)) - def instantiate(bound: Type, args: List[Type]) = - bound.LambdaAbstract(tparams).appliedTo(args) - checkBounds(orderedArgs, bounds, instantiate) - - def checkValidIfHKApply(implicit ctx: Context): Unit = - checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) - checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) - case Select(qual, name) if name.isTypeName => - checkRealizable(qual.tpe, qual.pos.focus) - case SingletonTypeTree(ref) => - checkRealizable(ref.tpe, ref.pos.focus) - case _ => - } - traverseChildren(tree) - } + def checkAppliedType(tree: AppliedTypeTree)(implicit ctx: Context) = { + val AppliedTypeTree(tycon, args) = tree + // If `args` is a list of named arguments, return corresponding type parameters, + // otherwise return type parameters unchanged + val tparams = tycon.tpe.typeParams + def argNamed(tparam: TypeParamInfo) = args.find { + case NamedArg(name, _) => name == tparam.paramName + case _ => false + }.getOrElse(TypeTree(tparam.paramRef)) + val orderedArgs = if (hasNamedArg(args)) tparams.map(argNamed) else args + val bounds = tparams.map(_.paramBoundsAsSeenFrom(tycon.tpe)) + def instantiate(bound: Type, args: List[Type]) = + bound.LambdaAbstract(tparams).appliedTo(args) + checkBounds(orderedArgs, bounds, instantiate) + + def checkWildcardHKApply(tp: Type, pos: Position): Unit = tp match { + case tp @ HKApply(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) => + tycon match { + case tycon: PolyType => + ctx.errorOrMigrationWarning( + ex"unreducible application of higher-kinded type $tycon to wildcard arguments", + pos) + case _ => + checkWildcardHKApply(tp.superType, pos) + } + case _ => + } + def checkValidIfHKApply(implicit ctx: Context): Unit = + checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) + checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) } - + /** Check that `tp` refers to a nonAbstract class * and that the instance conforms to the self type of the created class. */ diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 397b6d95b..c390ae808 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -138,7 +138,7 @@ object EtaExpansion { if (mt.paramTypes.length == xarity) mt.paramTypes map (_ => TypeTree()) else mt.paramTypes map TypeTree val params = (mt.paramNames, paramTypes).zipped.map((name, tpe) => - ValDef(name, TypeTree(tpe), EmptyTree).withFlags(Synthetic | Param).withPos(tree.pos)) + ValDef(name, tpe, EmptyTree).withFlags(Synthetic | Param).withPos(tree.pos)) var ids: List[Tree] = mt.paramNames map (name => Ident(name).withPos(tree.pos)) if (mt.paramTypes.nonEmpty && mt.paramTypes.last.isRepeatedParam) ids = ids.init :+ repeated(ids.last) diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala index 55008c0c5..40c1ca350 100644 --- a/src/dotty/tools/dotc/typer/Inliner.scala +++ b/src/dotty/tools/dotc/typer/Inliner.scala @@ -244,8 +244,9 @@ object Inliner { /** Replace `Inlined` node by a block that contains its bindings and expansion */ def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { val reposition = new TreeMap { - override def transform(tree: Tree)(implicit ctx: Context): Tree = - tree.withPos(inlined.call.pos) + override def transform(tree: Tree)(implicit ctx: Context): Tree = { + super.transform(tree).withPos(inlined.call.pos) + } } tpd.seq(inlined.bindings, reposition.transform(inlined.expansion)) } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 00e92cbfb..9da0e2edc 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -927,7 +927,7 @@ class Namer { typer: Typer => val tptProto = mdef.tpt match { case _: untpd.DerivedTypeTree => WildcardType - case TypeTree(untpd.EmptyTree) => + case TypeTree() => inferredType case TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) => val rhsType = typedAheadExpr(mdef.rhs, tpt.tpe).tpe diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 834bb37a8..026015d1d 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -851,15 +851,6 @@ class RefChecks extends MiniPhase { thisTransformer => tree } - override def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo) = { - if (!tree.original.isEmpty) - tree.tpe.foreachPart { - case tp: NamedType => checkUndesiredProperties(tp.symbol, tree.pos) - case _ => - } - tree - } - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { checkUndesiredProperties(tree.symbol, tree.pos) currentLevel.enterReference(tree.symbol, tree.pos) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 56c04c063..c283f43cf 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -953,28 +953,23 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { - if (tree.original.isEmpty) - tree match { - case tree: untpd.DerivedTypeTree => - tree.ensureCompletions - try - TypeTree(tree.derivedType(tree.attachment(untpd.OriginalSymbol))) withPos tree.pos - // btw, no need to remove the attachment. The typed - // tree is different from the untyped one, so the - // untyped tree is no longer accessed after all - // accesses with typedTypeTree are done. - catch { - case ex: NoSuchElementException => - println(s"missing OriginalSymbol for ${ctx.owner.ownersIterator.toList}") - throw ex - } - case _ => - assert(isFullyDefined(pt, ForceDegree.none)) - tree.withType(pt) - } - else { - val original1 = typed(tree.original) - cpy.TypeTree(tree)(original1).withType(original1.tpe) + tree match { + case tree: untpd.DerivedTypeTree => + tree.ensureCompletions + try + TypeTree(tree.derivedType(tree.attachment(untpd.OriginalSymbol))) withPos tree.pos + // btw, no need to remove the attachment. The typed + // tree is different from the untyped one, so the + // untyped tree is no longer accessed after all + // accesses with typedTypeTree are done. + catch { + case ex: NoSuchElementException => + println(s"missing OriginalSymbol for ${ctx.owner.ownersIterator.toList}") + throw ex + } + case _ => + assert(isFullyDefined(pt, ForceDegree.none)) + tree.withType(pt) } } |