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 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/dotty/tools/dotc/ast') 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)) } -- 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(-) (limited to 'src/dotty/tools/dotc/ast') 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 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(-) (limited to 'src/dotty/tools/dotc/ast') 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 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(-) (limited to 'src/dotty/tools/dotc/ast') 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 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(-) (limited to 'src/dotty/tools/dotc/ast') 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 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(-) (limited to 'src/dotty/tools/dotc/ast') 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