From 2cd85b99c10d7bdacbd5a9cfa4a10ca6ea082c0c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 20 Jul 2020 13:29:02 +0200 Subject: [PATCH] Remove all mixin traits from Contexts. In particular: - `newSymbol`, `requiredSymbol` etc, now are available from Symbols, no `ctx.` prefix needed - All reporing methods are available from `report` object. Also: - Change functions from Context to context functions. - Add atPhase, atPhaseNoLater, addPhaseNoEarlier and have them replace most uss of `withPhase`... - Add inMode, withMode, withoutMode utility wrappers - Move error messages directly into reporting: this avoids an annoying import - Convert old style implicit parameters to `(using Context)` - Reorganize TyperState.test: Instead of overwriting fields of TyperState, keep test contexts in an explicit stack, so that they can be re-used. This is simpler and since there is more decoupling between tests. Usage is now `Contexts.explore(...)` instead of `ctx.test(...)`. # Conflicts: # compiler/src/dotty/tools/dotc/core/Definitions.scala # Conflicts: # compiler/src/dotty/tools/dotc/Run.scala # compiler/src/dotty/tools/dotc/core/Annotations.scala # compiler/src/dotty/tools/dotc/core/Contexts.scala # compiler/src/dotty/tools/dotc/core/Definitions.scala # compiler/src/dotty/tools/dotc/core/Phases.scala # compiler/src/dotty/tools/dotc/core/TyperState.scala # compiler/src/dotty/tools/dotc/core/Types.scala # compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala # compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala # compiler/src/dotty/tools/dotc/transform/TreeChecker.scala # compiler/src/dotty/tools/dotc/typer/Namer.scala --- compiler/src/dotty/tools/dotc/Run.scala | 10 ++++----- .../dotty/tools/dotc/core/Annotations.scala | 5 +++++ .../src/dotty/tools/dotc/core/Contexts.scala | 20 +++++++----------- .../dotty/tools/dotc/core/Definitions.scala | 2 +- .../src/dotty/tools/dotc/core/Phases.scala | 10 ++++----- .../dotty/tools/dotc/core/TyperState.scala | 13 ++---------- .../tools/dotc/core/tasty/TreeUnpickler.scala | 16 +++++++------- .../dotc/reporting/ExploringReporter.scala | 21 +++++++++++++++++++ .../tools/dotc/transform/ExplicitOuter.scala | 17 ++++++++------- .../tools/dotc/transform/TreeChecker.scala | 6 +++--- .../src/dotty/tools/dotc/typer/Namer.scala | 8 +++---- 11 files changed, 71 insertions(+), 57 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/reporting/ExploringReporter.scala diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 1101dec8c15a..53c4be27d8ce 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -131,7 +131,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint throw ex } - /** TODO: There's a fundamental design problem here: We assemble phases using `squash` + /** TODO: There's a fundamental design problem here: We assemble phases using `fusePhases` * when we first build the compiler. But we modify them with -Yskip, -Ystop * on each run. That modification needs to either transform the tree structure, * or we need to assemble phases on each run, and take -Yskip, -Ystop into @@ -237,10 +237,10 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint private def printTree(last: PrintedTree)(using Context): PrintedTree = { val unit = ctx.compilationUnit val prevPhase = ctx.phase.prev // can be a mini-phase - val squashedPhase = ctx.base.squashed(prevPhase) + val fusedPhase = ctx.base.fusedContaining(prevPhase) val treeString = unit.tpdTree.show(using ctx.withProperty(XprintMode, Some(()))) - report.echo(s"result of $unit after $squashedPhase:") + report.echo(s"result of $unit after $fusedPhase:") last match { case SomePrintedTree(phase, lastTreeSting) if lastTreeSting != treeString => @@ -248,7 +248,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint if (!ctx.settings.XprintDiff.value && !ctx.settings.XprintDiffDel.value) treeString else DiffUtil.mkColoredCodeDiff(treeString, lastTreeSting, ctx.settings.XprintDiffDel.value) report.echo(msg) - SomePrintedTree(squashedPhase.toString, treeString) + SomePrintedTree(fusedPhase.toString, treeString) case SomePrintedTree(phase, lastTreeSting) => report.echo(" Unchanged since " + phase) @@ -256,7 +256,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint case NoPrintedTree => report.echo(treeString) - SomePrintedTree(squashedPhase.toString, treeString) + SomePrintedTree(fusedPhase.toString, treeString) } } diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 20ca963ff4a2..52b515ad0ae8 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -59,6 +59,11 @@ object Annotations { case symFn: (Context ?=> Symbol) @unchecked => mySym = null mySym = atPhaseNoLater(picklerPhase)(symFn) + // We should always produce the same annotation tree, no matter when the + // annotation is evaluated. Setting the phase to a pre-transformation phase + // seems to be enough to ensure this (note that after erasure, `ctx.typer` + // will be the Erasure typer, but that doesn't seem to affect the annotation + // trees we create, so we leave it as is) case sym: Symbol if sym.defRunId != parentCtx.runId => mySym = sym.denot.current.symbol case _ => diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index d043a6bd1a11..3b30bb0cbebf 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -305,7 +305,7 @@ object Contexts { private var phasedCtxs: Array[Context] = null /** This context at given phase. - * This method will always return a phase period equal to phaseId, thus will never return squashed phases + * This method will always return a phase period equal to phaseId, thus will never return a fused phase */ final def withPhase(phaseId: PhaseId): Context = if (this.period.phaseId == phaseId) this @@ -324,12 +324,6 @@ object Contexts { final def withPhase(phase: Phase): Context = withPhase(phase.id) - final def withPhaseNoLater(phase: Phase): Context = - if (phase.exists && period.phaseId > phase.id) withPhase(phase) else this - - final def withPhaseNoEarlier(phase: Phase): Context = - if (phase.exists && period.phaseId < phase.id) withPhase(phase) else this - // `creationTrace`-related code. To enable, uncomment the code below and the // call to `setCreationTrace()` in this file. /* @@ -662,8 +656,8 @@ object Contexts { def updateStore[T](loc: Store.Location[T], value: T): this.type = setStore(store.updated(loc, value)) - def setPhase(pid: PhaseId): this.type = setPeriod(Period(period.runId, pid)) - def setPhase(phase: Phase): this.type = setPeriod(Period(period.runId, phase.start, phase.end)) + def setPhase(pid: PhaseId): this.type = setPeriod(Period(runId, pid)) + def setPhase(phase: Phase): this.type = setPeriod(Period(runId, phase.start, phase.end)) def setSetting[T](setting: Setting[T], value: T): this.type = setSettings(setting.updateIn(settingsState, value)) @@ -709,7 +703,7 @@ object Contexts { testContexts(testsInUse).reuseIn(ctx) else val ts = TyperState() - .setReporter(TestingReporter()) + .setReporter(ExploringReporter()) .setCommittable(false) val c = FreshContext(ctx.base).init(ctx, ctx).setTyperState(ts) testContexts += c @@ -720,7 +714,7 @@ object Contexts { val result = try op(using nestedCtx) finally - nestedTS.reporter.asInstanceOf[TestingReporter].reset() + nestedTS.reporter.asInstanceOf[ExploringReporter].reset() testsInUse -= 1 result end explore @@ -796,7 +790,7 @@ object Contexts { definitions.init() } - def squashed(p: Phase): Phase = + def fusedContaining(p: Phase): Phase = allPhases.find(_.period.containsPhaseId(p.id)).getOrElse(NoPhase) } @@ -864,7 +858,7 @@ object Contexts { /** Phases by id */ private[dotc] var phases: Array[Phase] = _ - /** Phases with consecutive Transforms grouped into a single phase, Empty array if squashing is disabled */ + /** Phases with consecutive Transforms grouped into a single phase, Empty array if fusion is disabled */ private[core] var fusedPhases: Array[Phase] = Array.empty[Phase] /** Next denotation transformer id */ diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 08b25fb5edf0..1f7b51bf78e7 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -710,7 +710,7 @@ class Definitions { @tu lazy val InternalQuotedType_unapply: Symbol = InternalQuotedTypeModule.requiredMethod(nme.unapply) @tu lazy val QuotedTypeClass: ClassSymbol = requiredClass("scala.quoted.Type") - @tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.splice) + @tu lazy val QuotedType_splice: Symbol = QuotedTypeClass.requiredType(tpnme.spliceType) @tu lazy val QuotedTypeModule: Symbol = QuotedTypeClass.companionModule diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index d8878ef15a6a..994cc3ff05a3 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -92,13 +92,13 @@ object Phases { s"${phase.phaseName} requires ${unmetRequirements.mkString(", ")} to be in different TreeTransformer") case _ => - assert(false, s"Only tree transforms can be squashed, ${phase.phaseName} can not be squashed") + assert(false, s"Only tree transforms can be fused, ${phase.phaseName} can not be fused") } val superPhase = new MegaPhase(filteredPhaseBlock.asInstanceOf[List[MiniPhase]].toArray) prevPhases ++= filteredPhaseBlock.map(_.phaseName) superPhase } - else { // block of a single phase, no squashing + else { // block of a single phase, no fusion val phase = filteredPhaseBlock.head prevPhases += phase.phaseName phase @@ -118,9 +118,9 @@ object Phases { /** Use the following phases in the order they are given. * The list should never contain NoPhase. - * if squashing is enabled, phases in same subgroup will be squashed to single phase. + * if fusion is enabled, phases in same subgroup will be fused to single phase. */ - final def usePhases(phasess: List[Phase], squash: Boolean = true): Unit = { + final def usePhases(phasess: List[Phase], fuse: Boolean = true): Unit = { val flatPhases = collection.mutable.ListBuffer[Phase]() @@ -183,7 +183,7 @@ object Phases { nextDenotTransformerId(i) = lastTransformerId } - if (squash) + if (fuse) this.fusedPhases = (NoPhase :: phasess).toArray else this.fusedPhases = this.phases diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 168e415b1d43..ccb1cfd29648 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -29,7 +29,7 @@ class TyperState() { private var myId: Int = _ def id: Int = myId - private var previous: TyperState = _ + private var previous: TyperState /* | Null */ = _ private var myReporter: Reporter = _ @@ -69,7 +69,7 @@ class TyperState() { /** Initializes all fields except reporter, isCommittable, which need to be * set separately. */ - private[core] def init(previous: TyperState, constraint: Constraint): this.type = + private[core] def init(previous: TyperState /* | Null */, constraint: Constraint): this.type = this.myId = TyperState.nextId TyperState.nextId += 1 this.previous = previous @@ -79,15 +79,6 @@ class TyperState() { this.isCommitted = false this - def disable() = - previous = null - myConstraint = null - previousConstraint = null - myOwnedVars = null - isCommitted = false - myReporter = null - myIsCommittable = true - /** A fresh typer state with the same constraint as this one. */ def fresh(reporter: Reporter = StoreReporter(this.reporter)): TyperState = util.Stats.record("TyperState.fresh") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 0057ab275ae7..b5bf7128d552 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -116,9 +116,10 @@ class TreeUnpickler(reader: TastyReader, val owner = ctx.owner val source = ctx.source def complete(denot: SymDenotation)(using Context): Unit = - treeAtAddr(currentAddr) = + treeAtAddr(currentAddr) = atPhaseNoLater(picklerPhase) { new TreeReader(reader).readIndexedDef()( - using ctx.withPhaseNoLater(picklerPhase).withOwner(owner).withSource(source)) + using ctx.withOwner(owner).withSource(source)) + } } class TreeReader(val reader: TastyReader) { @@ -1375,11 +1376,12 @@ class TreeUnpickler(reader: TastyReader, op: TreeReader => Context ?=> T) extends Trees.Lazy[T] { def complete(using Context): T = { pickling.println(i"starting to read at ${reader.reader.currentAddr} with owner $owner") - op(reader)(using ctx - .withPhaseNoLater(picklerPhase) - .withOwner(owner) - .withModeBits(mode) - .withSource(source)) + atPhaseNoLater(picklerPhase) { + op(reader)(using ctx + .withOwner(owner) + .withModeBits(mode) + .withSource(source)) + } } } diff --git a/compiler/src/dotty/tools/dotc/reporting/ExploringReporter.scala b/compiler/src/dotty/tools/dotc/reporting/ExploringReporter.scala new file mode 100644 index 000000000000..e7b636cddf02 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/reporting/ExploringReporter.scala @@ -0,0 +1,21 @@ +package dotty.tools +package dotc +package reporting + +import collection.mutable +import core.Contexts.Context +import Diagnostic._ + +/** A re-usable Reporter used in Contexts#test */ +class ExploringReporter extends StoreReporter(null): + infos = new mutable.ListBuffer[Diagnostic] + + override def hasUnreportedErrors: Boolean = + infos.exists(_.isInstanceOf[Error]) + + override def removeBufferedMessages(using Context): List[Diagnostic] = + try infos.toList finally reset() + + def reset(): Unit = infos.clear() + +end ExploringReporter \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 6c85145faa5c..5ab591ca1e68 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -397,16 +397,19 @@ object ExplicitOuter { try @tailrec def loop(tree: Tree, count: Int): Tree = val treeCls = tree.tpe.widen.classSymbol - val outerAccessorCtx = ctx.withPhaseNoLater(lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore - report.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(using outerAccessorCtx)} in $treeCls") + report.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${atPhaseNoLater(lambdaLiftPhase)(outerAccName(treeCls.asClass))} in $treeCls") if (count == 0 || count < 0 && treeCls == toCls) tree else val enclClass = ctx.owner.lexicallyEnclosingClass.asClass - val outerAcc = tree match - case tree: This if tree.symbol == enclClass && !enclClass.is(Trait) => - outerParamAccessor(enclClass)(using outerAccessorCtx) - case _ => - outerAccessor(treeCls.asClass)(using outerAccessorCtx) + val outerAcc = atPhaseNoLater(lambdaLiftPhase) { + // lambdalift mangles local class names, which means we cannot + // reliably find outer acessors anymore + tree match + case tree: This if tree.symbol == enclClass && !enclClass.is(Trait) => + outerParamAccessor(enclClass) + case _ => + outerAccessor(treeCls.asClass) + } assert(outerAcc.exists, i"failure to construct path from ${ctx.owner.ownersIterator.toList}%/% to `this` of ${toCls.showLocated};\n${treeCls.showLocated} does not have an outer accessor") loop(tree.select(outerAcc).ensureApplied, count - 1) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 5e5ee1b9dd80..df1ec46bb4f6 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -100,7 +100,7 @@ class TreeChecker extends Phase with SymTransformer { cur.signature } assert(curSig == initial.signature, - i"""Signature of ${sym.showLocated} changed at phase ${ctx.base.squashed(ctx.phase.prev)} + i"""Signature of ${sym.showLocated} changed at phase ${ctx.base.fusedContaining(ctx.phase.prev)} |Initial info: ${initial} |Initial sig : ${initial.signature} |Current info: ${cur} @@ -133,8 +133,8 @@ class TreeChecker extends Phase with SymTransformer { def check(phasesToRun: Seq[Phase], ctx: Context): Tree = { val prevPhase = ctx.phase.prev // can be a mini-phase - val squashedPhase = ctx.base.squashed(prevPhase) - report.echo(s"checking ${ctx.compilationUnit} after phase ${squashedPhase}")(using ctx) + val fusedPhase = ctx.base.fusedContaining(prevPhase) + report.echo(s"checking ${ctx.compilationUnit} after phase ${fusedPhase}")(using ctx) inContext(ctx) { assertSelectWrapsNew(ctx.compilationUnit.tpdTree) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 43086777a77f..1519adef1309 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -669,7 +669,7 @@ class Namer { typer: Typer => given creationContext as Context = ictx // make sure testing contexts are not captured by completers - assert(!ictx.reporter.isInstanceOf[TestingReporter]) + assert(!ictx.reporter.isInstanceOf[ExploringReporter]) protected def typeSig(sym: Symbol): Type = original match { case original: ValDef => @@ -1213,10 +1213,8 @@ class Namer { typer: Typer => } def typedAheadType(tree: Tree, pt: Type = WildcardType)(using Context): tpd.Tree = - withoutMode(Mode.PatternOrTypeBits) { - withMode(Mode.Type) { - typedAhead(tree, typer.typed(_, pt)) - } + inMode(ctx.mode &~ Mode.PatternOrTypeBits | Mode.Type) { + typedAhead(tree, typer.typed(_, pt)) } def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(using Context): tpd.Tree =