From d2d2d8f9335c50f6b4e017d63d26e8841b3c4226 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 16 Nov 2017 00:46:58 +0100 Subject: [PATCH 1/8] Anonymous classes should not be called $anonfun --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 826256b30c6f..ca1dd57892ee 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -262,7 +262,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val parents1 = if (parents.head.classSymbol.is(Trait)) parents.head.parents.head :: parents else parents - val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_FUN, Synthetic, parents1, + val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_CLASS, Synthetic, parents1, coord = fns.map(_.pos).reduceLeft(_ union _)) val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil).entered def forwarder(fn: TermSymbol, name: TermName) = { From 21e90d86810d5a5617b466bb40487949a7f63004 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 16 Nov 2017 16:26:04 +0100 Subject: [PATCH 2/8] Add -Yprintpos-syms to print the position of symbols And add it to the set of options passed to the pickling tests so that we can start checking that symbol positions are properly pickled and unpickled. --- .../tools/dotc/config/ScalaSettings.scala | 1 + .../tools/dotc/printing/RefinedPrinter.scala | 21 ++++++++++++------- compiler/test/dotc/tests.scala | 2 +- .../tools/vulpix/TestConfiguration.scala | 3 ++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index e4f3137458ef..edc1f89e5f62 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -96,6 +96,7 @@ class ScalaSettings extends Settings.SettingGroup { val YdetailedStats = BooleanSetting("-Ydetailed-stats", "show detailed internal compiler stats (needs Stats.enabled to be set to true).") val Yheartbeat = BooleanSetting("-Ydetailed-stats", "show heartbeat stack trace of compiler operations (needs Stats.enabled to be set to true).") val YprintPos = BooleanSetting("-Yprint-pos", "show tree positions.") + val YprintPosSyms = BooleanSetting("-Yprint-pos-syms", "show symbol definitions positions.") val YnoDeepSubtypes = BooleanSetting("-Yno-deep-subtypes", "throw an exception on deep subtyping call stacks.") val YnoPatmatOpt = BooleanSetting("-Yno-patmat-opt", "disable all pattern matching optimizations.") val YplainPrinter = BooleanSetting("-Yplain-printer", "Pretty-print using a plain printer.") diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 705d94604c0b..3dabf69c2b67 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -13,6 +13,7 @@ import Trees._ import TypeApplications._ import Decorators._ import config.Config +import util.Positions._ import transform.SymUtils._ import scala.annotation.switch import language.implicitConversions @@ -630,14 +631,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else if (tree.isType && !homogenizedView) txt = toText(tp) } - if (printPos && !suppressPositions) { - // add positions - val pos = - if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic - else tree.pos - val clsStr = ""//if (tree.isType) tree.getClass.toString else "" - txt = (txt ~ "@" ~ pos.toString ~ clsStr).close - } + if (!suppressPositions) { + if (printPos) { + val pos = + if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic + else tree.pos + val clsStr = ""//if (tree.isType) tree.getClass.toString else "" + txt = (txt ~ "@" ~ pos.toString ~ clsStr).close + } + if (ctx.settings.YprintPosSyms.value && tree.isDef) + txt = (txt ~ + s"@@(${tree.symbol.name}=" ~ tree.symbol.pos.toString ~ ")").close + } if (ctx.settings.YshowTreeIds.value) txt = (txt ~ "#" ~ tree.uniqueId.toString).close tree match { diff --git a/compiler/test/dotc/tests.scala b/compiler/test/dotc/tests.scala index 6de3a86d2054..09c12deee091 100644 --- a/compiler/test/dotc/tests.scala +++ b/compiler/test/dotc/tests.scala @@ -73,7 +73,7 @@ class tests extends CompilerTest { else List("-Ycheck:tailrec,resolveSuper,mixin,elimStaticThis,labelDef,simplify") } ++ checkOptions ++ classPath - val testPickling = List("-Xprint-types", "-Ytest-pickler", "-Ystop-after:pickler", "-Yprint-pos") + val testPickling = List("-Xprint-types", "-Ytest-pickler", "-Ystop-after:pickler", "-Yprint-pos", "-Yprint-pos-syms") val twice = List("#runs", "2") val staleSymbolError: List[String] = List() diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index 75c2f6c87089..23b4479f9fd4 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -54,7 +54,8 @@ object TestConfiguration { val picklingOptions = defaultUnoptimised and ( "-Xprint-types", "-Ytest-pickler", - "-Yprint-pos" + "-Yprint-pos", + "-Yprint-pos-syms" ) val scala2Mode = defaultOptions and "-language:Scala2" val explicitUTF8 = defaultOptions and ("-encoding", "UTF8") From 5fdd40b1c77e97706ab1d20b60afcd689a80478b Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 21 Nov 2017 17:44:06 +0100 Subject: [PATCH 3/8] Give position to symbols when unpickling Previously, unpickled symbols always had position NoCoord, now their position is set based on the corresponding tree position. This does not always match the position used when they were pickled so `testPickling` currently fails, this is fixed in the subsequent commits of this PR. Symbol#isDefinedInCurrentRun is broken by this commit and fixed in a latter commit in this PR. --- .../src/dotty/tools/dotc/core/Symbols.scala | 13 +++-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 47 ++++++++++++------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 031146a80924..e66cf891c8f6 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -130,8 +130,8 @@ trait Symbols { this: Context => newClassSymbol(owner, name, flags, completer, privateWithin, coord, assocFile) } - def newRefinedClassSymbol = newCompleteClassSymbol( - ctx.owner, tpnme.REFINE_CLASS, NonMember, parents = Nil) + def newRefinedClassSymbol(coord: Coord = NoCoord) = + newCompleteClassSymbol(ctx.owner, tpnme.REFINE_CLASS, NonMember, parents = Nil, coord = coord) /** Create a module symbol with associated module class * from its non-info fields and a function producing the info @@ -444,6 +444,7 @@ object Symbols { /** Does this symbol come from a currently compiled source file? */ final def isDefinedInCurrentRun(implicit ctx: Context): Boolean = { + // FIXME: broken now now that symbols unpickled from TASTY have a position pos.exists && defRunId == ctx.runId } @@ -563,8 +564,12 @@ object Symbols { } } - /** The position of this symbol, or NoPosition is symbol was not loaded - * from source. + /** The position of this symbol, or NoPosition if the symbol was not loaded + * from source or from TASTY. This is always a zero-extent position. + * + * NOTE: If the symbol was not loaded from the current compilation unit, + * the implicit conversion `sourcePos` will return the wrong result, careful! + * TODO: Consider changing this method return type to `SourcePosition`. */ def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 735520ce3ac5..bd7beccf0855 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -259,7 +259,8 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi case TYPEARGtype => TypeArgRef(readType(), readType().asInstanceOf[TypeRef], readNat()) case BIND => - val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType()) + val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType(), + coord = coordAt(start)) registerSym(start, sym) if (currentAddr != end) readType() TypeRef(NoPrefix, sym) @@ -464,11 +465,14 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi rootd.symbol case _ => val completer = adjustIfModule(new Completer(ctx.owner, subReader(start, end))) + + val coord = coordAt(start) + if (isClass) - ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, privateWithin, coord = start.index) + ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, privateWithin, coord) else - ctx.newSymbol(ctx.owner, name, flags, completer, privateWithin, coord = start.index) - } // TODO set position somehow (but take care not to upset Symbol#isDefinedInCurrentRun) + ctx.newSymbol(ctx.owner, name, flags, completer, privateWithin, coord) + } sym.annotations = annots ctx.enter(sym) registerSym(start, sym) @@ -995,7 +999,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi case BIND => val name = readName() val info = readType() - val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, info) + val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, info, coord = coordAt(start)) registerSym(start, sym) Bind(sym, readTerm()) case ALTERNATIVE => @@ -1011,7 +1015,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi val argPats = until(end)(readTerm()) UnApply(fn, implicitArgs, argPats, patType) case REFINEDtpt => - val refineCls = ctx.newRefinedClassSymbol + val refineCls = ctx.newRefinedClassSymbol(coordAt(start)) typeAtAddr(start) = refineCls.typeRef val parent = readTpt() val refinements = readStats(refineCls, end)(localContext(refineCls)) @@ -1087,21 +1091,32 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi // ------ Setting positions ------------------------------------------------ - /** Set position of `tree` at given `addr`. */ - def setPos[T <: untpd.Tree](addr: Addr, tree: T)(implicit ctx: Context): tree.type = + /** Pickled position for `addr`. */ + def posAt(addr: Addr)(implicit ctx: Context): Position = if (ctx.mode.is(Mode.ReadPositions)) { posUnpicklerOpt match { case Some(posUnpickler) => - //println(i"setPos $tree / ${tree.getClass} at $addr to ${posUnpickler.posAt(addr)}") - val pos = posUnpickler.posAt(addr) - if (pos.exists) tree.setPosUnchecked(pos) - tree + posUnpickler.posAt(addr) case _ => - //println(i"no pos $tree") - tree + NoPosition } - } - else tree + } else NoPosition + + /** Coordinate for the symbol at `addr`. */ + def coordAt(addr: Addr)(implicit ctx: Context): Coord = { + val pos = posAt(addr) + if (pos.exists) + positionCoord(pos) + else + indexCoord(addr.index) + } + + /** Set position of `tree` at given `addr`. */ + def setPos[T <: untpd.Tree](addr: Addr, tree: T)(implicit ctx: Context): tree.type = { + val pos = posAt(addr) + if (pos.exists) tree.setPosUnchecked(pos) + tree + } } class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 46f96d7d3354..1519780ea8be 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -759,7 +759,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit args match { case ValDef(_, _, _) :: _ => typedDependent(args.asInstanceOf[List[ValDef]])( - ctx.fresh.setOwner(ctx.newRefinedClassSymbol).setNewScope) + ctx.fresh.setOwner(ctx.newRefinedClassSymbol(tree.pos)).setNewScope) case _ => typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funCls.typeRef), args :+ body), pt) } From 026e7cee38fd46cf589183cb16cf1031c1ad5111 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 21 Nov 2017 17:30:38 +0100 Subject: [PATCH 4/8] Always pickle the position of DefTrees A symbol is created before the corresponding tree is unpickled, so we cannot rely on initialPos to set the symbol position. Instead, we always pickle the position of definitions. In practice, this means that we now always pickle the position of all `TypeDef` and `Bind` (the other `DefTree`s are all `WithLazyField` and thus already had their positions pickled). --- .../tools/dotc/core/tasty/PositionPickler.scala | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 546894a9efca..6fec7b0337b1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -41,11 +41,21 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr lastPos = pos } - /** True if x's position cannot be reconstructed automatically from its initialPos + /** True if x's position shouldn't be reconstructed automatically from its initialPos */ def alwaysNeedsPos(x: Positioned) = x match { - case _: WithLazyField[_] // initialPos is inaccurate for trees with lazy field - | _: Trees.PackageDef[_] => true // package defs might be split into several Tasty files + case + // initialPos is inaccurate for trees with lazy field + _: WithLazyField[_] + + // A symbol is created before the corresponding tree is unpickled, + // and its position cannot be changed afterwards. + // so we cannot use the tree initialPos to set the symbol position. + // Instead, we always pickle the position of definitions. + | _: Trees.DefTree[_] + + // package defs might be split into several Tasty files + | _: Trees.PackageDef[_] => true case _ => false } From 1841b3db0e5dbca340295505b7fdfc3a4591cf78 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 17 Nov 2017 00:13:11 +0100 Subject: [PATCH 5/8] Fix the .pos.point of `Bind` nodes The point of a definition should be the position of the first letter in the name of a definition. For a `Bind` node this is just the start (previously we were using the position of the `:`). --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 311834121aa4..171ccb641e14 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1181,7 +1181,8 @@ object Parsers { t } - def ascription(t: Tree, location: Location.Value) = atPos(startOffset(t), in.skipToken()) { + def ascription(t: Tree, location: Location.Value) = atPos(startOffset(t)) { + in.skipToken() in.token match { case USCORE => val uscoreStart = in.skipToken() From 87ffb886a21b96c674c6dc9a04376f21acfc7135 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 17 Nov 2017 00:16:15 +0100 Subject: [PATCH 6/8] Fixes to synchronize the position of trees and symbols Various changes so that `testPickling` with `-Yprintpos-syms` now passes. It's not always obvious what position to use when new trees are created, here are the principles we used here: - When a definition tree is replaced by another definition tree (for example, in `ParamForwarding`), the new tree position is set to the old tree position. - When a reference tree is replaced by a definition tree representing an accessor and a new reference tree that calls the accessor (for example in `SuperAccessors`), the accessor position is set to the old tree *point*, and the new reference tree position is set to the old tree *position*. This makes sense because we already use zero-extent positions for compiler artifacts that the IDE should ignore and an accessor is a compilation artifact. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- .../src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../dotc/transform/ParamForwarding.scala | 2 +- .../tools/dotc/transform/SuperAccessors.scala | 20 +++++++++++-------- .../dotc/transform/SyntheticMethods.scala | 2 +- .../dotty/tools/dotc/typer/EtaExpansion.scala | 4 ++-- .../src/dotty/tools/dotc/typer/Inliner.scala | 13 +++++++----- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index ca1dd57892ee..b27c16d0eb00 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -196,7 +196,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case tp: MethodType => def valueParam(name: TermName, info: Type): TermSymbol = { val maybeImplicit = if (tp.isImplicitMethod) Implicit else EmptyFlags - ctx.newSymbol(sym, name, TermParam | maybeImplicit, info) + ctx.newSymbol(sym, name, TermParam | maybeImplicit, info, coord = sym.coord) } val params = (tp.paramNames, tp.paramInfos).zipped.map(valueParam) val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef))) diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index e66cf891c8f6..f282e12ecbbc 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -288,7 +288,7 @@ trait Symbols { this: Context => val tparamBuf = new mutable.ListBuffer[TypeSymbol] val trefBuf = new mutable.ListBuffer[TypeRef] for (name <- names) { - val tparam = newNakedSymbol[TypeName](NoCoord) + val tparam = newNakedSymbol[TypeName](owner.coord) tparamBuf += tparam trefBuf += TypeRef(owner.thisType, tparam) } diff --git a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala index 52ef9b1d9a0a..b876a3a9b47e 100644 --- a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala +++ b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -70,7 +70,7 @@ class ParamForwarding(thisPhase: DenotTransformer) { val superAcc = Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) typr.println(i"adding param forwarder $superAcc") - DefDef(sym, superAcc.ensureConforms(sym.info.widen)) + DefDef(sym, superAcc.ensureConforms(sym.info.widen)).withPos(stat.pos) } return forwarder(ctx.withPhase(thisPhase.next)) } diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index 404709d2cc91..7049079e34b9 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -76,6 +76,7 @@ class SuperAccessors(thisPhase: DenotTransformer) { if (clazz is Trait) superName = superName.expandedName(clazz) val superInfo = sel.tpe.widenSingleton.ensureMethodic + val accPos = sel.pos.focus val superAcc = clazz.info.decl(superName) .suchThat(_.signature == superInfo.signature).symbol .orElse { @@ -83,11 +84,11 @@ class SuperAccessors(thisPhase: DenotTransformer) { val deferredOrPrivate = if (clazz is Trait) Deferred else Private val acc = ctx.newSymbol( clazz, superName, Artifact | Method | deferredOrPrivate, - superInfo, coord = sym.coord).enteredAfter(thisPhase) + superInfo, coord = accPos).enteredAfter(thisPhase) // Diagnostic for SI-7091 if (!accDefs.contains(clazz)) ctx.error(s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz is Package}. Accessor required for ${sel} (${sel.show})", sel.pos) - else accDefs(clazz) += DefDef(acc, EmptyTree) + else accDefs(clazz) += DefDef(acc, EmptyTree).withPos(accPos) acc } @@ -181,13 +182,14 @@ class SuperAccessors(thisPhase: DenotTransformer) { } accTypeOf(sym.info) } + val accPos = sel.pos.focus val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { val newAcc = ctx.newSymbol( - clazz, accName, Artifact | Method, accType, coord = sel.pos).enteredAfter(thisPhase) + clazz, accName, Artifact | Method, accType, coord = accPos).enteredAfter(thisPhase) val code = polyDefDef(newAcc, trefs => vrefss => { val (receiver :: _) :: tail = vrefss val base = receiver.select(sym).appliedToTypes(trefs) - (base /: tail)(Apply(_, _)) + (base /: tail)(Apply(_, _)).withPos(accPos) }) ctx.debuglog("created protected accessor: " + code) accDefs(clazz) += code @@ -233,13 +235,14 @@ class SuperAccessors(thisPhase: DenotTransformer) { MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, mt.newParamRef(0))) } val accType = accTypeOf(sym.info) + val accPos = tree.pos.focus val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { val newAcc = ctx.newSymbol( - clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisPhase) + clazz, accName, Artifact, accType, coord = accPos).enteredAfter(thisPhase) val code = polyDefDef(newAcc, trefs => vrefss => { val (receiver :: _) :: tail = vrefss val base = receiver.select(sym).appliedToTypes(trefs) - (base /: vrefss)(Apply(_, _)) + (base /: vrefss)(Apply(_, _)).withPos(accPos) }) ctx.debuglog("created protected accessor: " + code) accDefs(clazz) += code @@ -265,12 +268,13 @@ class SuperAccessors(thisPhase: DenotTransformer) { val accName = ProtectedSetterName(field.name) val accType = MethodType(clazz.classInfo.selfType :: field.info :: Nil, defn.UnitType) + val accPos = tree.pos.focus val protectedAccessor = clazz.info.decl(accName).symbol orElse { val newAcc = ctx.newSymbol( - clazz, accName, Artifact, accType, coord = tree.pos).enteredAfter(thisPhase) + clazz, accName, Artifact, accType, coord = accPos).enteredAfter(thisPhase) val code = DefDef(newAcc, vrefss => { val (receiver :: value :: Nil) :: Nil = vrefss - Assign(receiver.select(field), value).withPos(tree.pos) + Assign(receiver.select(field), value).withPos(accPos) }) ctx.debuglog("created protected setter: " + code) accDefs(clazz) += code diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index ac87ffc91984..d2ca704d3612 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -99,7 +99,7 @@ class SyntheticMethods(thisPhase: DenotTransformer) { case nme.productElement => vrefss => productElementBody(accessors.length, vrefss.head.head) } ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") - DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))) + DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))).withPos(ctx.owner.pos.focus) } /** The class diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 828b559a47ae..d198341884d0 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -28,8 +28,8 @@ object EtaExpansion { val name = UniqueName.fresh(prefix) val liftedType = fullyDefinedType(expr.tpe.widen, "lifted expression", expr.pos) val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, liftedType, coord = positionCoord(expr.pos)) - defs += ValDef(sym, expr) - ref(sym.termRef) + defs += ValDef(sym, expr).withPos(expr.pos.focus) + ref(sym.termRef).withPos(expr.pos) } /** Lift out common part of lhs tree taking part in an operator assignment such as diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index e174c9a11b19..3240b3556e10 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -98,8 +98,8 @@ object Inliner { val accessorType = accessedType.ensureMethodic val accessor = accessorSymbol(tree, accessorType).asTerm val accessorDef = polyDefDef(accessor, tps => argss => - rhs(refPart, tps, argss)) - val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss) + rhs(refPart, tps, argss).withPos(tree.pos.focus)) + val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss).withPos(tree.pos) (accessorDef, accessorRef) } else { // Hard case: Reference needs to go via a dynamic prefix @@ -135,11 +135,14 @@ object Inliner { val accessor = accessorSymbol(tree, accessorType).asTerm val accessorDef = polyDefDef(accessor, tps => argss => - rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail)) + rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail) + .withPos(tree.pos.focus) + ) val accessorRef = ref(accessor) .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs) .appliedToArgss((qual :: Nil) :: argss) + .withPos(tree.pos) (accessorDef, accessorRef) } accessors += accessorDef @@ -168,8 +171,8 @@ object Inliner { // Draft code (incomplete): // // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType - // myAccessors += TypeDef(accessor) - // ref(accessor) + // myAccessors += TypeDef(accessor).withPos(tree.pos.focus) + // ref(accessor).withPos(tree.pos) // tree } From 888c24b4115dd5bc05fb228155a281d100db1d64 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 21 Nov 2017 20:27:01 +0100 Subject: [PATCH 7/8] Fix ExpandPrivate when compiling from TASTY When compiling from TASTY, `ctx.source.file` will be the TASTY file corresponding to the current compilation unit, this will never match `d.symbol.sourceFile` whose value is set from the `SourceFile` annotation in the TASTY tree. Fixed by using `ctx.owner.sourceFile` instead. The added test, `anonBridge.scala` needs this fix to work. It also needs the previous work done in this PR to give positions to unpickled symbols, otherwise it crashes in `Typer#assertPositioned` because `Bridges#addBridge` creates a tree whose position comes from a symbol. --- compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala | 4 ++-- tests/pos-from-tasty/anonBridge.scala | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 tests/pos-from-tasty/anonBridge.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala index f49495c80d3f..34bc6c6e2694 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala @@ -90,8 +90,8 @@ class ExpandPrivate extends MiniPhase with IdentityDenotTransformer { thisPhase } assert(d.symbol.sourceFile != null && - isSimilar(d.symbol.sourceFile.path, ctx.source.file.path), - s"private ${d.symbol.showLocated} in ${d.symbol.sourceFile} accessed from ${ctx.owner.showLocated} in ${ctx.source.file}") + isSimilar(d.symbol.sourceFile.path, ctx.owner.sourceFile.path), + s"private ${d.symbol.showLocated} in ${d.symbol.sourceFile} accessed from ${ctx.owner.showLocated} in ${ctx.owner.sourceFile}") d.ensureNotPrivate.installAfter(thisPhase) } diff --git a/tests/pos-from-tasty/anonBridge.scala b/tests/pos-from-tasty/anonBridge.scala new file mode 100644 index 000000000000..c11a46a8904f --- /dev/null +++ b/tests/pos-from-tasty/anonBridge.scala @@ -0,0 +1,3 @@ +class Test { + val x: PartialFunction[Int, Int] = { case x: Int => x } +} From 6b19932c31f2932430cdaf77d127e1fc7db8ded1 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 1 Dec 2017 14:32:31 +0100 Subject: [PATCH 8/8] Fix Symbol#isDefinedInCurrentRun And add a test to make sure it's working. The test required a change to the testing infrastructure: files in compilation groups should be sorted alphabetically, otherwise we cannot know in advance if the error will happen in A1.scala or A2.scala --- compiler/src/dotty/tools/dotc/Run.scala | 54 +++++++++++++++---- .../tools/dotc/core/ConstraintRunInfo.scala | 1 + .../src/dotty/tools/dotc/core/Contexts.scala | 8 +-- .../src/dotty/tools/dotc/core/Symbols.scala | 11 ++-- .../dotty/tools/dotc/fromtasty/TASTYRun.scala | 4 +- .../dotc/interactive/InteractiveDriver.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 1 + .../test/dotty/tools/dotc/CompilerTest.scala | 2 +- .../dotty/tools/vulpix/ParallelTesting.scala | 5 +- tests/neg/checkNoConflict/A1.scala | 2 + tests/neg/checkNoConflict/A2.scala | 2 + 11 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 tests/neg/checkNoConflict/A1.scala create mode 100644 tests/neg/checkNoConflict/A2.scala diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 62c8bbe70cde..7794d36c87e3 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -10,14 +10,15 @@ import Types._ import Scopes._ import typer.{FrontEnd, Typer, ImportInfo, RefChecks} import Decorators._ -import io.PlainFile +import io.{AbstractFile, PlainFile} import scala.io.Codec -import util._ +import util.{Set => _, _} import reporting.Reporter import transform.TreeChecker import rewrite.Rewrites import java.io.{BufferedWriter, OutputStreamWriter} import printing.XprintMode +import typer.ImplicitRunInfo import scala.annotation.tailrec import dotty.tools.io.VirtualFile @@ -25,8 +26,7 @@ import scala.util.control.NonFatal /** A compiler run. Exports various methods to compile source files */ class Run(comp: Compiler, ictx: Context) { - - var units: List[CompilationUnit] = _ + import Run._ /** Produces the following contexts, from outermost to innermost * @@ -78,7 +78,7 @@ class Run(comp: Compiler, ictx: Context) { compileSources(sources) } catch { case NonFatal(ex) => - ctx.echo(i"exception occurred while compiling $units%, %") + ctx.echo(i"exception occurred while compiling ${ctx.runInfo.units}%, %") throw ex } @@ -90,17 +90,17 @@ class Run(comp: Compiler, ictx: Context) { */ def compileSources(sources: List[SourceFile]) = if (sources forall (_.exists)) { - units = sources map (new CompilationUnit(_)) + ctx.runInfo.units = sources map (new CompilationUnit(_)) compileUnits() } def compileUnits(us: List[CompilationUnit]): Unit = { - units = us + ctx.runInfo.units = us compileUnits() } def compileUnits(us: List[CompilationUnit], ctx: Context): Unit = { - units = us + ctx.runInfo.units = us compileUnits()(ctx) } @@ -122,16 +122,16 @@ class Run(comp: Compiler, ictx: Context) { if (phase.isRunnable) Stats.trackTime(s"$phase ms ") { val start = System.currentTimeMillis - units = phase.runOn(units) + ctx.runInfo.units = phase.runOn(ctx.runInfo.units) if (ctx.settings.Xprint.value.containsPhase(phase)) { - for (unit <- units) { + for (unit <- ctx.runInfo.units) { lastPrintedTree = printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) } } ctx.informTime(s"$phase ", start) Stats.record(s"total trees at end of $phase", ast.Trees.ntrees) - for (unit <- units) + for (unit <- ctx.runInfo.units) Stats.record(s"retained typed trees at end of $phase", unit.tpdTree.treeSize) } } @@ -191,3 +191,35 @@ class Run(comp: Compiler, ictx: Context) { r } } + +object Run { + /** Info that changes on each compiler run */ + class RunInfo(initctx: Context) extends ImplicitRunInfo with ConstraintRunInfo { + implicit val ctx: Context = initctx + + private[this] var myUnits: List[CompilationUnit] = _ + private[this] var myUnitsCached: List[CompilationUnit] = _ + private[this] var myFiles: Set[AbstractFile] = _ + + /** The compilation units currently being compiled, this may return different + * results over time. + */ + def units: List[CompilationUnit] = myUnits + + private[Run] def units_=(us: List[CompilationUnit]): Unit = + myUnits = us + + + /** The files currently being compiled, this may return different results over time. + * These files do not have to be source files since it's possible to compile + * from TASTY. + */ + def files: Set[AbstractFile] = { + if (myUnits ne myUnitsCached) { + myUnitsCached = myUnits + myFiles = myUnits.map(_.source.file).toSet + } + myFiles + } + } +} diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala b/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala index 041faf911857..c2b44dd189c6 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala @@ -2,6 +2,7 @@ package dotty.tools.dotc package core import Contexts._ +import Run.RunInfo import config.Printers.typr trait ConstraintRunInfo { self: RunInfo => diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 439c04d502a0..fe5f5b6d4c62 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -14,11 +14,12 @@ import NameOps._ import Uniques._ import SymDenotations._ import Comments._ +import Run.RunInfo import util.Positions._ import ast.Trees._ import ast.untpd import util.{FreshNameCreator, SimpleIdentityMap, SourceFile, NoSource} -import typer.{Implicits, ImplicitRunInfo, ImportInfo, Inliner, NamerContextOps, SearchHistory, TypeAssigner, Typer} +import typer.{Implicits, ImportInfo, Inliner, NamerContextOps, SearchHistory, TypeAssigner, Typer} import Implicits.ContextualImplicits import config.Settings._ import config.Config @@ -675,11 +676,6 @@ object Contexts { // @sharable val theBase = new ContextBase // !!! DEBUG, so that we can use a minimal context for reporting even in code that normally cannot access a context } - /** Info that changes on each compiler run */ - class RunInfo(initctx: Context) extends ImplicitRunInfo with ConstraintRunInfo { - implicit val ctx: Context = initctx - } - class GADTMap(initBounds: SimpleIdentityMap[Symbol, TypeBounds]) extends util.DotClass { private[this] var myBounds = initBounds def setBounds(sym: Symbol, b: TypeBounds): Unit = diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index f282e12ecbbc..4b8730ead2f5 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -443,10 +443,11 @@ object Symbols { if (lastDenot == null) NoRunId else lastDenot.validFor.runId /** Does this symbol come from a currently compiled source file? */ - final def isDefinedInCurrentRun(implicit ctx: Context): Boolean = { - // FIXME: broken now now that symbols unpickled from TASTY have a position - pos.exists && defRunId == ctx.runId - } + final def isDefinedInCurrentRun(implicit ctx: Context): Boolean = + pos.exists && defRunId == ctx.runId && { + val file = associatedFile + file != null && ctx.runInfo.files.contains(file) + } /** Is symbol valid in current run? */ final def isValidInCurrentRun(implicit ctx: Context): Boolean = @@ -540,7 +541,7 @@ object Symbols { * Overridden in ClassSymbol */ def associatedFile(implicit ctx: Context): AbstractFile = - denot.topLevelClass.symbol.associatedFile + if (lastDenot == null) null else lastDenot.topLevelClass.symbol.associatedFile /** The class file from which this class was generated, null if not applicable. */ final def binaryFile(implicit ctx: Context): AbstractFile = { diff --git a/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala b/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala index 9b360ea1056f..08562b067e45 100644 --- a/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala +++ b/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala @@ -6,7 +6,7 @@ import core.Contexts._ class TASTYRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) { override def compile(classNames: List[String]) = { - units = classNames.map(new TASTYCompilationUnit(_)) - compileUnits() + val units = classNames.map(new TASTYCompilationUnit(_)) + compileUnits(units) } } diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index 6e3206ba684e..b55b34298f5f 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -226,7 +226,7 @@ class InteractiveDriver(settings: List[String]) extends Driver { run.compileSources(List(source)) run.printSummary() - val t = run.units.head.tpdTree + val t = ctx.runInfo.units.head.tpdTree cleanup(t) myOpenedTrees(uri) = topLevelClassTrees(t, source) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 259fb1048cf4..1344daec0b31 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -9,6 +9,7 @@ import util.Stats.{track, record, monitored} import printing.{Showable, Printer} import printing.Texts._ import Contexts._ +import Run.RunInfo import Types._ import Flags._ import TypeErasure.{erasure, hasStableErasure} diff --git a/compiler/test/dotty/tools/dotc/CompilerTest.scala b/compiler/test/dotty/tools/dotc/CompilerTest.scala index d6c2bbbc80f1..259e1487c1a7 100644 --- a/compiler/test/dotty/tools/dotc/CompilerTest.scala +++ b/compiler/test/dotty/tools/dotc/CompilerTest.scala @@ -104,7 +104,7 @@ abstract class CompilerTest { else (fp, jfp) } val expErrors = expectedErrors(filePaths.toList) - (filePaths, javaFilePaths, normArgs, expErrors) + (filePaths.sorted, javaFilePaths.sorted, normArgs, expErrors) } if (runTest) log(s"WARNING: run tests can only be run by partest, JUnit just verifies compilation: $prefix$dirName") diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 903a098c34d0..1c82ef2bd12d 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -151,7 +151,8 @@ trait ParallelTesting extends RunnerOrchestration { self => ) extends TestSource { /** Get the files grouped by `_X` as a list of groups, files missing this - * suffix will be put into the same group + * suffix will be put into the same group. + * Files in each group are sorted alphabetically. * * Filters out all none source files */ @@ -170,7 +171,7 @@ trait ParallelTesting extends RunnerOrchestration { self => .toOption .getOrElse("") } - .toList.sortBy(_._1).map(_._2.filter(isSourceFile)) + .toList.sortBy(_._1).map(_._2.filter(isSourceFile).sorted) } /** Each `Test` takes the `testSources` and performs the compilation and assertions diff --git a/tests/neg/checkNoConflict/A1.scala b/tests/neg/checkNoConflict/A1.scala new file mode 100644 index 000000000000..f86f19f5009e --- /dev/null +++ b/tests/neg/checkNoConflict/A1.scala @@ -0,0 +1,2 @@ +package foo +class A diff --git a/tests/neg/checkNoConflict/A2.scala b/tests/neg/checkNoConflict/A2.scala new file mode 100644 index 000000000000..68cea1f71ab2 --- /dev/null +++ b/tests/neg/checkNoConflict/A2.scala @@ -0,0 +1,2 @@ +package foo +class A // error: class A in package foo has already been compiled once during this run