From 2575d11bd1a670ace8bd7e91777ea135759af51f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 13 Oct 2016 09:39:44 +0200 Subject: Preserve all positions in Tasty. The goal is that pickled and unpickled trees should print the same with -Yprintpos. There are several reasons why this is not the case so far. Some of them are fixed in this commit. --- src/dotty/tools/dotc/ast/Positioned.scala | 4 +- .../tools/dotc/core/tasty/PositionPickler.scala | 18 +++--- src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 7 ++- src/dotty/tools/dotc/core/tasty/TreeBuffer.scala | 4 +- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 36 ++++++++---- .../tools/dotc/core/tasty/TreeUnpickler.scala | 66 ++++++++++++++-------- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 8 ++- src/dotty/tools/dotc/transform/Pickler.scala | 7 ++- 8 files changed, 98 insertions(+), 52 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Positioned.scala b/src/dotty/tools/dotc/ast/Positioned.scala index 8d364d439..f6793a309 100644 --- a/src/dotty/tools/dotc/ast/Positioned.scala +++ b/src/dotty/tools/dotc/ast/Positioned.scala @@ -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/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 63bb00a71..65e9d12be 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -37,23 +37,27 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr lastIndex = index lastPos = pos } - def traverse(x: Any, parentPos: Position): Unit = x match { + def traverse(x: Any): Unit = x match { case x: Tree @unchecked => - if (x.pos.exists && x.pos.toSynthetic != parentPos.toSynthetic) { + if (x.pos.exists /*&& x.pos.toSynthetic != x.initialPos.toSynthetic*/) { addrOfTree(x) match { - case Some(addr) => pickleDeltas(addr.index, x.pos) + case Some(addr) => + //println(i"pickling $x") + pickleDeltas(addr.index, x.pos) case _ => + //println(i"no address for $x") } } + //else println(i"skipping $x") x match { - case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos) + case x: MemberDef @unchecked => traverse(x.symbol.annotations) 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/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 8e8d58b47..fc551658b 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* @@ -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 diff --git a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala index d741c42c3..f8f7c330f 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) { @@ -19,6 +19,8 @@ class TreeBuffer extends TastyBuffer(50000) { private[tasty] val pickledTrees = new java.util.IdentityHashMap[Tree, Any] // Value type is really Addr, but that's not compatible with null + def registerTreeAddr(tree: Tree) = pickledTrees.put(tree, currentAddr) + def addrOfTree(tree: Tree): Option[Addr] = pickledTrees.get(tree) match { case null => None case n => Some(n.asInstanceOf[Addr]) diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8889e8a5c..215cef295 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 @@ -299,7 +300,10 @@ 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 = { + pickledTrees.put(tpt, currentAddr) + pickleType(tpt.tpe) // TODO correlate with original when generating positions + } def pickleTreeUnlessEmpty(tree: Tree)(implicit ctx: Context): Unit = if (!tree.isEmpty) pickleTree(tree) @@ -313,17 +317,20 @@ class TreePickler(pickler: TastyPickler) { pickleParams tpt match { case tpt: TypeTree => pickleTpt(tpt) - case _ => pickleTree(tpt) + case templ: Template => pickleTree(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 = { + pickledTrees.put(tree, currentAddr) + 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 pickleParams(trees: List[Tree])(implicit ctx: Context): Unit = { @@ -500,12 +507,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 +524,12 @@ class TreePickler(pickler: TastyPickler) { throw ex } + def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = { + pickledTrees.put(id, currentAddr) + 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..7038f6e0a 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -626,7 +626,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle * or else read definition. */ def readIndexedDef()(implicit ctx: Context): Tree = treeAtAddr.remove(currentAddr) match { - case Some(tree) => skipTree(); tree + case Some(tree) => skipTree(); setPos(currentAddr, tree) case none => readNewDef() } @@ -655,12 +655,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 +688,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 +706,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 = @@ -752,6 +760,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val parentRefs = ctx.normalizeToClassRefs(parents.map(_.tpe), cls, cls.unforcedDecls) val self = if (nextByte == SELFDEF) { + val selfStart = currentAddr readByte() untpd.ValDef(readName(), readTpt(), EmptyTree).withType(NoType) } @@ -816,21 +825,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] = @@ -1002,11 +1018,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} 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..7f88e246b 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -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,10 @@ 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 + if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) { + val pos = if (homogenizedView) tree.pos.toSynthetic else tree.pos + txt = txt ~ "@" ~ pos.toString + } tree match { case Block(_, _) | Template(_, _, _, _) => txt case _ => txt.close diff --git a/src/dotty/tools/dotc/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala index 90e62b65c..2fb85b6c0 100644 --- a/src/dotty/tools/dotc/transform/Pickler.scala +++ b/src/dotty/tools/dotc/transform/Pickler.scala @@ -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) } } -- cgit v1.2.3 From 8b55988c64ab7d777b6bf39987af6bafe070c2fb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 12 Oct 2016 14:37:37 +0200 Subject: Drop original on TypeTree The plan is to keep original type until after pickling, and afterwards replace it with a simple TypeTree. # Conflicts: # src/dotty/tools/dotc/core/tasty/TreePickler.scala --- src/dotty/tools/dotc/ast/Trees.scala | 15 +++------ src/dotty/tools/dotc/ast/tpd.scala | 7 ++-- src/dotty/tools/dotc/ast/untpd.scala | 5 ++- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 +-- src/dotty/tools/dotc/transform/TailRec.scala | 2 +- src/dotty/tools/dotc/transform/TreeTransform.scala | 5 +-- src/dotty/tools/dotc/typer/EtaExpansion.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 2 +- src/dotty/tools/dotc/typer/RefChecks.scala | 9 ----- src/dotty/tools/dotc/typer/Typer.scala | 39 ++++++++++------------ 11 files changed, 33 insertions(+), 59 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 2c02e7d1e..dca66a01d 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -520,13 +520,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 +959,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 +1101,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 +1205,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 852c3a346..7fd818c25 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -144,7 +144,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 @@ -205,8 +205,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/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 215cef295..99a83a57c 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -444,7 +444,7 @@ class TreePickler(pickler: TastyPickler) { // 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) => + case TypeTree() => pickleTpt(tree) case Bind(name, body) => registerDef(tree.symbol) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7f88e246b..91a789aef 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -355,8 +355,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else "/* inlined from " ~ toText(call) ~ "*/ " ~ blockText(bindings :+ body) case tpt: untpd.DerivedTypeTree => "" - 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) => diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index d99a48af3..a7a8ac177 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -350,7 +350,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/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/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 4d82a2d12..a2bb9b8d0 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 6be119319..f6d53f1e2 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -961,28 +961,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) } } -- cgit v1.2.3 From bc0bdb198269f040bd0b7fc0abeb30447fd2abe1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 13 Oct 2016 11:14:51 +0200 Subject: Record position of self type Also, some simplifying refactorings and renamings --- src/dotty/tools/dotc/core/tasty/TreeBuffer.scala | 14 +++++++------- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 18 +++++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala index f8f7c330f..b9e1d2b45 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala @@ -17,11 +17,11 @@ 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 val treeAddr = new java.util.IdentityHashMap[Tree, Any] // Value type is really Addr, but that's not compatible with null - def registerTreeAddr(tree: Tree) = pickledTrees.put(tree, currentAddr) + def registerTreeAddr(tree: Tree) = treeAddr.put(tree, currentAddr) - def addrOfTree(tree: Tree): Option[Addr] = pickledTrees.get(tree) match { + def addrOfTree(tree: Tree): Option[Addr] = treeAddr.get(tree) match { case null => None case n => Some(n.asInstanceOf[Addr]) } @@ -149,11 +149,11 @@ class TreeBuffer extends TastyBuffer(50000) { wasted } - def adjustPickledTrees(): Unit = { - val it = pickledTrees.keySet.iterator + def adjustTreeAddrs(): Unit = { + val it = treeAddr.keySet.iterator while (it.hasNext) { val tree = it.next - pickledTrees.put(tree, adjusted(pickledTrees.get(tree).asInstanceOf[Addr])) + treeAddr.put(tree, adjusted(treeAddr.get(tree).asInstanceOf[Addr])) } } @@ -174,7 +174,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 99a83a57c..18920ec30 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -153,6 +153,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) @@ -300,10 +305,8 @@ class TreePickler(pickler: TastyPickler) { pickled } - def pickleTpt(tpt: Tree)(implicit ctx: Context): Unit = { - pickledTrees.put(tpt, currentAddr) - 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) @@ -325,7 +328,7 @@ class TreePickler(pickler: TastyPickler) { } def pickleParam(tree: Tree)(implicit ctx: Context): Unit = { - pickledTrees.put(tree, currentAddr) + registerTreeAddr(tree) tree match { case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt) case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs) @@ -344,7 +347,7 @@ class TreePickler(pickler: TastyPickler) { } def pickleTree(tree: Tree)(implicit ctx: Context): Unit = try { - pickledTrees.put(tree, currentAddr) + registerTreeAddr(tree) tree match { case Ident(name) => tree.tpe match { @@ -493,6 +496,7 @@ class TreePickler(pickler: TastyPickler) { if ((selfInfo ne NoType) || !tree.self.isEmpty) { writeByte(SELFDEF) pickleName(tree.self.name) + registerTreeAddr(tree.self) pickleType { cinfo.selfInfo match { case sym: Symbol => sym.info @@ -525,7 +529,7 @@ class TreePickler(pickler: TastyPickler) { } def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = { - pickledTrees.put(id, currentAddr) + registerTreeAddr(id) writeByte(tag) pickleName(id.name) } -- cgit v1.2.3 From f98b847cb2fca7d82cd24c869f4ba5a60c49d8b1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 16 Oct 2016 13:23:59 +0200 Subject: Handle shared trees Shared trees are pickled under multiple addresses. Previously, only the last address was stored, which led to trees with unknown positions. Now, all addresses are stored. --- .../tools/dotc/core/tasty/PositionPickler.scala | 23 ++++++++++++++-- src/dotty/tools/dotc/core/tasty/TastyPickler.scala | 6 ++-- src/dotty/tools/dotc/core/tasty/TreeBuffer.scala | 32 ++++++++++++++++------ src/dotty/tools/dotc/transform/Pickler.scala | 4 +-- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 65e9d12be..b528b8aaf 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -13,12 +13,29 @@ 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._ + 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) = { def toInt(b: Boolean) = if (b) 1 else 0 (addrDelta << 2) | (toInt(hasStartDelta) << 1) | toInt(hasEndDelta) @@ -40,9 +57,9 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr def traverse(x: Any): Unit = x match { case x: Tree @unchecked => if (x.pos.exists /*&& x.pos.toSynthetic != x.initialPos.toSynthetic*/) { - addrOfTree(x) match { + nextTreeAddr(x) match { case Some(addr) => - //println(i"pickling $x") + //println(i"pickling $x ar $addr") pickleDeltas(addr.index, x.pos) case _ => //println(i"no address for $x") 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 b9e1d2b45..f2681ecde 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala @@ -17,13 +17,26 @@ class TreeBuffer extends TastyBuffer(50000) { private var delta: Array[Int] = _ private var numOffsets = 0 - private val treeAddr = 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 registerTreeAddr(tree: Tree) = treeAddr.put(tree, currentAddr) - - def addrOfTree(tree: Tree): Option[Addr] = treeAddr.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)) @@ -150,10 +163,13 @@ class TreeBuffer extends TastyBuffer(50000) { } def adjustTreeAddrs(): Unit = { - val it = treeAddr.keySet.iterator + val it = treeAddrs.keySet.iterator while (it.hasNext) { val tree = it.next - treeAddr.put(tree, adjusted(treeAddr.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)) + } } } diff --git a/src/dotty/tools/dotc/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala index 2fb85b6c0..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 { -- cgit v1.2.3 From 1ad3803e3610ee249a0b95016639d97c85817e69 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Oct 2016 11:20:59 +0200 Subject: Fix pickling of annotation positions --- src/dotty/tools/dotc/core/tasty/PositionPickler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index b528b8aaf..64552ef27 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -59,7 +59,7 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] if (x.pos.exists /*&& x.pos.toSynthetic != x.initialPos.toSynthetic*/) { nextTreeAddr(x) match { case Some(addr) => - //println(i"pickling $x ar $addr") + //println(i"pickling $x at $addr") pickleDeltas(addr.index, x.pos) case _ => //println(i"no address for $x") @@ -67,7 +67,7 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] } //else println(i"skipping $x") x match { - case x: MemberDef @unchecked => traverse(x.symbol.annotations) + case x: MemberDef @unchecked => traverse(x.symbol.annotations.map(_.tree)) case _ => } traverse(x.productIterator) -- cgit v1.2.3 From 531f3c42c40ea05b412cbbdbcc020264dd2a4375 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Oct 2016 11:22:03 +0200 Subject: Fix pickling of self type position Was: position of self def, now: position of tpt in self def. --- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 18920ec30..a5f421888 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -496,7 +496,7 @@ class TreePickler(pickler: TastyPickler) { if ((selfInfo ne NoType) || !tree.self.isEmpty) { writeByte(SELFDEF) pickleName(tree.self.name) - registerTreeAddr(tree.self) + if (!tree.self.isEmpty) registerTreeAddr(tree.self.tpt) pickleType { cinfo.selfInfo match { case sym: Symbol => sym.info -- cgit v1.2.3 From 52b6daad86eb9ab5bbeecb2709729d992ee977f7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Oct 2016 12:33:00 +0200 Subject: Drop erroneous setPos in TreeUnpickler. With that change, all files tested in pickleOK have the same positions before and after pickling. --- src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 4 ++-- test/dotc/tests.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 7038f6e0a..57c0fe32d 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -626,7 +626,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle * or else read definition. */ def readIndexedDef()(implicit ctx: Context): Tree = treeAtAddr.remove(currentAddr) match { - case Some(tree) => skipTree(); setPos(currentAddr, tree) + case Some(tree) => skipTree(); tree case none => readNewDef() } @@ -1022,7 +1022,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle if (ctx.mode.is(Mode.ReadPositions)) { posUnpicklerOpt match { case Some(posUnpickler) => - //println(i"setPos $tree / ${tree.getClass} to ${posUnpickler.posAt(addr)}") + //println(i"setPos $tree / ${tree.getClass} at $addr to ${posUnpickler.posAt(addr)}") val pos = posUnpickler.posAt(addr) if (pos.exists) tree.setPosUnchecked(pos) tree diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 94b74d786..feb65021d 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -65,7 +65,7 @@ class tests extends CompilerTest { Directory(defaultOutputDir + "java").deleteRecursively() } - @Test def pickle_pickleOK = compileDir(testsDir, "pickling", testPickling) + @Test def pickle_pickleOK = compileDir(testsDir, "pickling", "-Yprintpos" :: testPickling) // This directory doesn't exist anymore // @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling) @Test def pickle_ast = compileDir(dotcDir, "ast", testPickling) -- cgit v1.2.3 From e2ce0dbb695df7ebad37ab41d2dd264c63ee6b36 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Oct 2016 15:43:38 +0200 Subject: Keep position always with term When printing with -Yprintpos always have the position follow immediately the term with the position. Makes it easier to interpret diffs. --- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 91a789aef..8b34b3ef8 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -527,7 +527,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) { val pos = if (homogenizedView) tree.pos.toSynthetic else tree.pos - txt = txt ~ "@" ~ pos.toString + txt = (txt ~ "@" ~ pos.toString).close } tree match { case Block(_, _) | Template(_, _, _, _) => txt -- cgit v1.2.3 From 2fe0f674124c5be064d1cbf80594fb8e7d5d78be Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Oct 2016 15:48:59 +0200 Subject: Fix dropInlined to also change positions of subtrees --- src/dotty/tools/dotc/typer/Inliner.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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)) } -- cgit v1.2.3 From 6ce403eb0c6b630f925db38b7538e0f9b8c892ea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Oct 2016 15:49:42 +0200 Subject: Pickle Inlined trees Inlined trees should be preserved by pickling so that we keep positions accurate. With that change now all tasty tests are tested for position accuracy. --- src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 3 +++ src/dotty/tools/dotc/core/tasty/TreePickler.scala | 13 ++++--------- src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 19 +++++++++++++------ test/dotc/tests.scala | 4 ++-- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index fc551658b..a8725136d 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -79,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* @@ -306,6 +307,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 @@ -456,6 +458,7 @@ object TastyFormat { case LAMBDA => "LAMBDA" case MATCH => "MATCH" case RETURN => "RETURN" + case INLINED => "INLINED" case TRY => "TRY" case REPEATED => "REPEATED" case BIND => "BIND" diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index a5f421888..7f1895cc3 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -438,15 +438,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 Inlined(call, bindings, expansion) => + writeByte(INLINED) + bindings.foreach(preRegister) + withLength { pickleTree(call); pickleTree(expansion); bindings.foreach(pickleTree) } case TypeTree() => pickleTpt(tree) case Bind(name, body) => diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 57c0fe32d..35613c08d 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -905,6 +905,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 => @@ -937,12 +946,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 = readTerm() + readBlock((defs, expr) => Inlined(call, defs.asInstanceOf[List[MemberDef]], expr)) case IF => If(readTerm(), readTerm(), readTerm()) case LAMBDA => diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index feb65021d..01539aa5a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -28,7 +28,7 @@ class tests extends CompilerTest { else List("-Ycheck:tailrec,resolveSuper,mixin,restoreScopes,labelDef") } - val testPickling = List("-Xprint-types", "-Ytest-pickler", "-Ystop-after:pickler") + val testPickling = List("-Xprint-types", "-Ytest-pickler", "-Ystop-after:pickler", "-Yprintpos") val twice = List("#runs", "2") val staleSymbolError: List[String] = List() @@ -65,7 +65,7 @@ class tests extends CompilerTest { Directory(defaultOutputDir + "java").deleteRecursively() } - @Test def pickle_pickleOK = compileDir(testsDir, "pickling", "-Yprintpos" :: testPickling) + @Test def pickle_pickleOK = compileDir(testsDir, "pickling", testPickling) // This directory doesn't exist anymore // @Test def pickle_pickling = compileDir(coreDir, "pickling", testPickling) @Test def pickle_ast = compileDir(dotcDir, "ast", testPickling) -- cgit v1.2.3 From 68608986741a3b32e2b1a328466e15ec0e7ab5b8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 17 Oct 2016 18:37:33 +0200 Subject: Minimize inline call info in PostTyper --- src/dotty/tools/dotc/transform/PostTyper.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 51851a589..fe542ca0a 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -224,6 +224,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) -- cgit v1.2.3 From fc6b8fba13a4ac1823763410568a48e46e4b2192 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 18 Oct 2016 00:23:15 +0200 Subject: Mention Inlined transformation in docs --- src/dotty/tools/dotc/ast/Trees.scala | 4 +++- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- src/dotty/tools/dotc/transform/PostTyper.scala | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index dca66a01d..4c63a67ce 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -502,7 +502,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. * diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 8b34b3ef8..31d45d42c 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -527,7 +527,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) { val pos = if (homogenizedView) tree.pos.toSynthetic else tree.pos - txt = (txt ~ "@" ~ pos.toString).close + txt = (txt ~ "@" ~ pos.toString /*~ tree.getClass.toString*/).close } tree match { case Block(_, _) | Template(_, _, _, _) => txt diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index fe542ca0a..e4447509b 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 -- cgit v1.2.3 From be29c982e43261873a57149371a2d75966d14802 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 18 Oct 2016 00:27:56 +0200 Subject: Don't generate positions that can be reconstructed Don't generate positions that can be reconstructed from the initial position of a tree. --- src/dotty/tools/dotc/core/tasty/PositionPickler.scala | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 64552ef27..78c15eae2 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -54,9 +54,16 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] lastIndex = index lastPos = pos } + + /** True if x's position cannot be reconstructed automatically from its initialPos + */ + def needsPosition(x: Positioned) = + x.pos.toSynthetic != x.initialPos.toSynthetic || + x.isInstanceOf[WithLazyField[_]] || // initialPos is inaccurate for trees with lazy fields + x.isInstanceOf[Trees.PackageDef[_]] // package defs might be split into several Tasty files def traverse(x: Any): Unit = x match { case x: Tree @unchecked => - if (x.pos.exists /*&& x.pos.toSynthetic != x.initialPos.toSynthetic*/) { + if (x.pos.exists && needsPosition(x)) { nextTreeAddr(x) match { case Some(addr) => //println(i"pickling $x at $addr") @@ -65,7 +72,7 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] //println(i"no address for $x") } } - //else println(i"skipping $x") + //else if (x.pos.exists) println(i"skipping $x") x match { case x: MemberDef @unchecked => traverse(x.symbol.annotations.map(_.tree)) case _ => -- cgit v1.2.3 From 3e545d7378568bc853b7ee865dbf42fd65bafe12 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Oct 2016 14:58:57 +0200 Subject: Maintain point for positions of definitions This is needed to figure out where the defined name is in a definition. --- src/dotty/tools/dotc/ast/Positioned.scala | 2 +- .../tools/dotc/core/tasty/PositionPickler.scala | 24 +++++++++++++--------- .../tools/dotc/core/tasty/PositionUnpickler.scala | 11 ++++++---- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 8 +++++--- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Positioned.scala b/src/dotty/tools/dotc/ast/Positioned.scala index f6793a309..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) } diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 78c15eae2..24a6c998e 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -36,9 +36,9 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] if (it.hasNext) Some(it.next) else None } - def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean) = { + 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) = { @@ -48,26 +48,30 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[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 } /** True if x's position cannot be reconstructed automatically from its initialPos */ - def needsPosition(x: Positioned) = - x.pos.toSynthetic != x.initialPos.toSynthetic || - x.isInstanceOf[WithLazyField[_]] || // initialPos is inaccurate for trees with lazy fields - x.isInstanceOf[Trees.PackageDef[_]] // package defs might be split into several Tasty files + 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 _ => false + } + def traverse(x: Any): Unit = x match { case x: Tree @unchecked => - if (x.pos.exists && needsPosition(x)) { + 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 at $addr") - pickleDeltas(addr.index, x.pos) + //println(i"pickling $x with $pos at $addr") + pickleDeltas(addr.index, pos) case _ => //println(i"no address for $x") } 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/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 31d45d42c..83b3da0b7 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -351,8 +351,8 @@ 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 => "" case TypeTree() => @@ -526,7 +526,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else if (!tree.isDef) txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close } if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) { - val pos = if (homogenizedView) tree.pos.toSynthetic else tree.pos + val pos = + if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic + else tree.pos txt = (txt ~ "@" ~ pos.toString /*~ tree.getClass.toString*/).close } tree match { -- cgit v1.2.3 From 08658cb20fee008217966830c51a0ff56ee1ae72 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Oct 2016 15:00:46 +0200 Subject: Updated .gitignore Updated to ignore two .cache files in ScalaIDE 4.4 --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 416a12eff..9536d0ebc 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ build/ # Ignore build-file .packages +/.cache-main +/.cache-tests -- cgit v1.2.3 From 59e83ce49fbb81f402be7d663f8f38bed0f556df Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Oct 2016 13:58:00 +0200 Subject: Don't convert to TypeTrees before pickling If we want to pickle types with positions we should not converyt to TypeTrees before pickling. Instead, type trees are now converted to TypeTrees in FirstTransform. --- .../tools/dotc/core/tasty/PositionPickler.scala | 1 + src/dotty/tools/dotc/core/tasty/TreePickler.scala | 6 +-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 11 +---- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 5 ++- .../tools/dotc/transform/FirstTransform.scala | 10 ++++- src/dotty/tools/dotc/transform/PostTyper.scala | 47 ++++++++++++++-------- src/dotty/tools/dotc/typer/Checking.scala | 12 +++--- 7 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 24a6c998e..6eb798721 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -61,6 +61,7 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] 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 } diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 7f1895cc3..a1ac69e0d 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -319,8 +319,8 @@ class TreePickler(pickler: TastyPickler) { pickleName(sym) pickleParams tpt match { - case tpt: TypeTree => pickleTpt(tpt) case templ: Template => pickleTree(tpt) + case _ if tpt.isType => pickleTpt(tpt) } pickleTreeUnlessEmpty(rhs) pickleModifiers(sym) @@ -349,6 +349,8 @@ class TreePickler(pickler: TastyPickler) { def pickleTree(tree: Tree)(implicit ctx: Context): Unit = try { registerTreeAddr(tree) tree match { + case tree if tree.isType => + pickleTpt(tree) case Ident(name) => tree.tpe match { case tp: TermRef => pickleType(tp) @@ -442,8 +444,6 @@ class TreePickler(pickler: TastyPickler) { writeByte(INLINED) bindings.foreach(preRegister) withLength { pickleTree(call); pickleTree(expansion); bindings.foreach(pickleTree) } - case TypeTree() => - pickleTpt(tree) case Bind(name, body) => registerDef(tree.symbol) writeByte(BIND) diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 35613c08d..c2779ba88 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -383,12 +383,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] @@ -760,7 +754,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val parentRefs = ctx.normalizeToClassRefs(parents.map(_.tpe), cls, cls.unforcedDecls) val self = if (nextByte == SELFDEF) { - val selfStart = currentAddr readByte() untpd.ValDef(readName(), readTpt(), EmptyTree).withType(NoType) } @@ -869,7 +862,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) @@ -948,7 +941,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle case BLOCK => readBlock(Block) case INLINED => - val call = readTerm() + val call = setPos(currentAddr, TypeTree(readType())) readBlock((defs, expr) => Inlined(call, defs.asInstanceOf[List[MemberDef]], expr)) case IF => If(readTerm(), readTerm(), readTerm()) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 83b3da0b7..7aaf2e190 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -525,11 +525,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (tree.isType) txt = toText(tp) else if (!tree.isDef) txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close } + 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 - txt = (txt ~ "@" ~ pos.toString /*~ tree.getClass.toString*/).close + val clsStr = "" // DEBUG: if (tree.isType) tree.getClass.toString else "" + txt = (txt ~ "@" ~ pos.toString ~ clsStr).close } tree match { case Block(_, _) | Template(_, _, _, _) => txt diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index 74dc9b9d6..adadf3e76 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -146,7 +146,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 +162,15 @@ 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 tree + + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = + if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree + // invariants: all modules have companion objects // all types are TypeTrees // all this types are explicit diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index e4447509b..650bc4968 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -76,8 +76,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // TODO fill in } - /** Check bounds of AppliedTypeTrees and TypeApplys. - * Replace type trees with TypeTree nodes. + /** Check bounds of AppliedTypeTrees. * Replace constant expressions with Literal nodes. * Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose. * Example @@ -105,18 +104,15 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran * 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 - } - } + private def normalizeTree(tree: Tree)(implicit ctx: Context): Tree = + if (tree.isType) { + Checking.typeCheck(tree) + tree + } + 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 @@ -203,12 +199,12 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran override def transform(tree: Tree)(implicit ctx: Context): Tree = try normalizeTree(tree) match { - case tree: Ident => + 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 => + case tree: Select if !tree.isType => transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil) case tree: Super => if (ctx.owner.enclosingMethod.isInlineMethod) @@ -284,6 +280,25 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran super.transform(tree) case tree @ Annotated(annotated, annot) => cpy.Annotated(tree)(transform(annotated), transformAnnot(annot)) + 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) + Checking.checkBounds(orderedArgs, bounds, instantiate) + + def checkValidIfHKApply(implicit ctx: Context): Unit = + Checking.checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) + checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) + super.transform(tree) + case tree: TypeTree => tree.withType( tree.tpe match { diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 7899174f5..b6c9e0047 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -79,7 +79,12 @@ object Checking { */ val typeChecker = new TreeTraverser { def traverse(tree: Tree)(implicit ctx: Context) = { - tree match { + typeCheck(tree) + traverseChildren(tree) + } + } + + def typeCheck(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 @@ -103,10 +108,7 @@ object Checking { checkRealizable(ref.tpe, ref.pos.focus) case _ => } - traverseChildren(tree) - } - } - + /** Check that `tp` refers to a nonAbstract class * and that the instance conforms to the self type of the created class. */ -- cgit v1.2.3 From 7967aa97607964f78c92911038412192cfea3778 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Oct 2016 14:50:11 +0200 Subject: Don't convert to literals before pickling We need to keep the original expressions around for accurate linking with the source file. That holds also if the expression has a constant type, so it should not be converted to a literal before pickling. Instead it will be converted in FirstTransform. --- src/dotty/tools/dotc/ast/TreeInfo.scala | 69 ++++++++++++++++++---- .../tools/dotc/transform/FirstTransform.scala | 22 ++++++- src/dotty/tools/dotc/transform/PostTyper.scala | 45 +++----------- 3 files changed, 86 insertions(+), 50 deletions(-) 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/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index adadf3e76..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._ @@ -166,10 +168,24 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota } override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = - if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree + 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 tree + 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 diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 650bc4968..b72785dce 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -77,42 +77,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } /** Check bounds of AppliedTypeTrees. - * 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 = - if (tree.isType) { - Checking.typeCheck(tree) - tree - } - else tree.tpe.widenTermRefExpr match { - case ConstantType(value) if isIdempotentExpr(tree) => Literal(value) - case _ => tree - } + private def normalizeTree(tree: Tree)(implicit ctx: Context): Tree = { + if (tree.isType) Checking.typeCheck(tree) + tree + } /** If the type of `tree` is a TermRefWithSignature with an underdefined * signature, narrow the type by re-computing the signature (which should @@ -158,7 +127,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) + } } } -- cgit v1.2.3 From d4c26fc6072e69d3f58685e6bfec4609eb9af13c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Oct 2016 15:08:35 +0200 Subject: Roll normalizedTree into main PostTyper transform Since we now transform all type trees, no need to have a separate traversal for checking. --- src/dotty/tools/dotc/transform/PostTyper.scala | 43 +++++-------- src/dotty/tools/dotc/typer/Checking.scala | 87 +++++++++++--------------- 2 files changed, 50 insertions(+), 80 deletions(-) diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index b72785dce..6b9fc084e 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -76,13 +76,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // TODO fill in } - /** Check bounds of AppliedTypeTrees. - */ - private def normalizeTree(tree: Tree)(implicit ctx: Context): Tree = { - if (tree.isType) Checking.typeCheck(tree) - 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). @@ -171,14 +164,19 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } override def transform(tree: Tree)(implicit ctx: Context): Tree = - try normalizeTree(tree) match { + 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 if !tree.isType => - 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) @@ -241,7 +239,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran tree } else { - Checking.typeChecker.traverse(tree.rhs) + //Checking.typeChecker.traverse(tree.rhs) cpy.TypeDef(tree)(rhs = TypeTree(tree.symbol.info)) } super.transform(tree1) @@ -253,25 +251,12 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran super.transform(tree) case tree @ Annotated(annotated, annot) => cpy.Annotated(tree)(transform(annotated), transformAnnot(annot)) - 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) - Checking.checkBounds(orderedArgs, bounds, instantiate) - - def checkValidIfHKApply(implicit ctx: Context): Unit = - Checking.checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) - checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) + 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/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index b6c9e0047..753475d84 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -55,60 +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) = { - typeCheck(tree) - 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)) } - def typeCheck(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 _ => - } - /** Check that `tp` refers to a nonAbstract class * and that the instance conforms to the self type of the created class. */ -- cgit v1.2.3 From 0c3a6788ababab2ac10c3dbf76cd3c088ff0582c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 31 Oct 2016 14:46:14 +0100 Subject: Keep rhs of TypeDefs until pickling Don't convert to a TypeTree in PostTyper, because that loses internal positions. --- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 8 ++++-- src/dotty/tools/dotc/transform/PostTyper.scala | 33 +++++++++++------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index a1ac69e0d..f0d8da9df 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -49,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) @@ -332,7 +336,7 @@ class TreePickler(pickler: TastyPickler) { 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) + case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, rhs(tree)) } } @@ -474,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) diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 6b9fc084e..49727e70b 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -223,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) -- cgit v1.2.3 From 0fd8804b3a2ce9a2099a3d7c1b756fec637f9d1c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 26 Oct 2016 16:26:21 +0200 Subject: Make cloned trees have new uniqueIds They used to share the same id as the tree they were cloned from, which makes id's not really unique. --- src/dotty/tools/dotc/ast/Trees.scala | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 4c63a67ce..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 { -- cgit v1.2.3 From 8b10844da492292d1fd70bf94f639fe70f88fcd2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 31 Oct 2016 15:09:34 +0100 Subject: Eliminate LAMBDAtype from TASTYformat Merge with POLYtypem, as we do already for internal types. --- src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 12 +++++------- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 9 +-------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index a8725136d..f9743d9d2 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -139,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 @@ -325,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 @@ -489,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/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index f0d8da9df..9dfb78798 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -262,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) diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index c2779ba88..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))( -- cgit v1.2.3 From d694f15a028cf14ea0cf210d3113c7b1d6af54e7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 31 Oct 2016 15:53:15 +0100 Subject: Address review comment Other comments had already been addressed in previous commits. --- src/dotty/tools/dotc/core/tasty/PositionPickler.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 6eb798721..4b67bc188 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -79,7 +79,8 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] } //else if (x.pos.exists) println(i"skipping $x") x match { - case x: MemberDef @unchecked => traverse(x.symbol.annotations.map(_.tree)) + case x: MemberDef @unchecked => + for (ann <- x.symbol.annotations) traverse(ann.tree) case _ => } traverse(x.productIterator) -- cgit v1.2.3