Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo {

val pluginPlan = ctx.base.addPluginPhases(ctx.base.phasePlan)
val phases = ctx.base.fusePhases(pluginPlan,
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, stopAfter, ctx.settings.Ycheck.value)
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.Ycheck.value)
ctx.base.usePhases(phases, runCtx)

if ctx.settings.YnoDoubleBindings.value then
Expand All @@ -390,7 +390,16 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo {
if (ctx.isBestEffort && phases.exists(_.phaseName == "typer")) Some("typer")
else None

for phase <- allPhases do
def matchesStopAfter(p: Phase): Boolean = p match
case mp: dotty.tools.dotc.transform.MegaPhase =>
mp.miniPhases.exists(sub => stopAfter.contains(sub.phaseName))
case _ =>
stopAfter.contains(p.phaseName)

var stopped = false
var i = 0
while i < allPhases.length && !stopped do
val phase = allPhases(i)
doEnterPhase(phase)
val phaseWillRun = phase.isRunnable || forceReachPhaseMaybe.nonEmpty
if phaseWillRun then
Expand Down Expand Up @@ -423,7 +432,9 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo {
end if
end if
doAdvancePhase(phase, wasRan = phaseWillRun)
end for
if matchesStopAfter(phase) then stopped = true
i += 1
end while
profiler.finished()
}

Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1396,13 +1396,15 @@ class CheckCaptures extends Recheck, SymTransformer:
* conforms to the expected type where all inferred capture sets are dropped.
* This ensures that if files compile separately, they will also compile
* in a joint compilation.
* 2. If a val has an inferred type with a terminal capability in its span capset,
* check that it this capability is subsumed by the capset that was inferred
* 2. If a val has an inferred type with a terminal capability in its capture set,
* check that this capability is subsumed by the capset that was inferred
* for the class from its other fields via `captureSetImpliedByFields`.
* That capset is defined to take into account all fields but is computed
* only from fields with explicitly given types in order to avoid cycles.
* See comment on Setup.fieldsWithExplicitTypes. So we have to make sure
* that fields with inferred types would not change that capset.
* REPL wrapper objects are exempt since they are invisible to the user
* and should not impose explicit type requirements on REPL definitions.
*/
def checkInferredResult(tp: Type, tree: ValOrDefDef)(using Context): Type = {
val sym = tree.symbol
Expand Down Expand Up @@ -1451,6 +1453,7 @@ class CheckCaptures extends Recheck, SymTransformer:
cls.isPackageObject && cls.enclosingPackageClass.isEmptyPackage
if sym.owner.isClass
&& !isToplevelDefsInEmptyPackage(sym.owner)
&& !sym.owner.name.isReplWrapperName // REPL wrappers are invisible to the user
&& contributesLocalCapToClass(sym)
&& !CaptureSet.isAssumedPure(sym)
then
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ private sealed trait YSettings:
val Yskip: Setting[List[String]] = PhasesSetting(ForkSetting, "Yskip", "Skip")
val YbackendParallelism: Setting[Int] = IntChoiceSetting(ForkSetting, "Ybackend-parallelism", "maximum worker threads for backend", 1 to 16, 1)
val YbackendWorkerQueue: Setting[Int] = IntChoiceSetting(ForkSetting, "Ybackend-worker-queue", "backend threads worker queue size", 0 to 1000, 0)
val YstopAfter: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-after", "Stop after", aliases = List("-stop")) // backward compat
val YstopAfter: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-after", "Stop after the phase group containing the named phase. Mini-phases fused into a MegaPhase share a group, so the rest of that group still runs.", aliases = List("-stop")) // backward compat
val YstopBefore: Setting[List[String]] = PhasesSetting(ForkSetting, "Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully
val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.")
val YlogicalPackageLoading: Setting[Boolean] = BooleanSetting(ForkSetting, "Ylogical-package-loading", "Enable logical package loading. This will load the logical package structure by preparsing the source files to discover the package structure. To be used together with -sourcepath option.")
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ object Phases {
final def fusePhases(phasess: List[List[Phase]],
phasesToSkip: List[String],
stopBeforePhases: List[String],
stopAfterPhases: List[String],
YCheckAfter: List[String])(using Context): List[Phase] = {
val fusedPhases = ListBuffer[Phase]()
var prevPhases: Set[String] = Set.empty
Expand All @@ -90,7 +89,7 @@ object Phases {

val filteredPhases = phasess.map(_.filter { p =>
try isEnabled(p)
finally stop |= stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName)
finally stop |= stopBeforePhases.contains(p.phaseName)
})

var i = 0
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/interactive/Completion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,11 @@ object Completion:
if qual.symbol.is(Package) then
directMemberCompletions(adjustedQual)
else if qual.typeOpt.hasSimpleKind then
def safeExtensionCompletions =
try extensionCompletions(adjustedQual)
catch case _: TypeError => Map.empty
implicitConversionMemberCompletions(adjustedQual) ++
//safeExtensionCompletions ++
extensionCompletions(adjustedQual) ++
directMemberCompletions(adjustedQual) ++
namedTupleCompletions(adjustedQual)
Expand Down Expand Up @@ -736,7 +740,7 @@ object Completion:
interactiv.println(i"implicit conversion targets considered: ${conversions.toList}%, %")
conversions
} catch case ex: Exception =>
logger.warning(
logger.fine(
s"Exception when searching for implicit conversions:\n ${ex.getMessage()}\n${ex.getStackTrace().mkString("\n")}"
)
Set.empty
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -573,10 +573,11 @@ class PlainPrinter(_ctx: Context) extends Printer {
case hi => hi.derivesFromCapSet)

/** Print capture variable bounds using `^` syntax.
* Plain CapSet lower bound and universal upper bound are elided.
* Plain CapSet lower bound, empty lower bound, and universal upper bound are elided.
*/
private def toTextCaptureVarBounds(lo: Type, hi: Type): Text =
val loText = lo match
case CapturingType(_, refs: CaptureSet) if refs.elems.isEmpty && !ccVerbose => Text() // empty lower bound
case CapturingType(_, refs) => " >: " ~ toTextCaptureSet(refs)
case _ => Text() // plain CapSet = trivial lower bound
val hiText = hi match
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
val (printPure, refsText) =
if refs == null then (isPure, Str(""))
else if isElidableUniversal(refs) then (false, Str(""))
else (isPure, toTextGeneralCaptureSet(refs))
else refs match
case cs: CaptureSet if cs.isConst && cs.elems.isEmpty => (true, Str(""))
case _ => (isPure, toTextGeneralCaptureSet(refs))
arrow(isContextual, printPure) ~ refsText

private def toTextFunction(args: List[Type], res: Type, fn: MethodType | AppliedType,
Expand Down
8 changes: 4 additions & 4 deletions docs/_docs/contributing/debugging/inspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ Sometimes you may want to stop the compiler after a certain phase, for example t
knock-on errors from occurring from a bug in an earlier phase. Use the flag
`-Ystop-after:<phase-name>` to prevent any phases executing afterwards.

> e.g. `-Vprint:<phase>` where `phase` is a miniphase, will print after
> the whole phase group is complete, which may be several miniphases after `phase`.
> Instead you can use `-Ystop-after:<phase> -Vprint:<phase>` to stop
> immediately after the miniphase and see the trees that you intended.
> Note: `-Ystop-after:<miniphase>` stops after the whole phase group containing
> `<miniphase>` (a MegaPhase fuses its mini-phases into one traversal).
> Combined with `-Vprint:<miniphase>`, the printed trees reflect the state at
> the end of the group, which may include mini-phases after `<miniphase>`.

## Printing TASTy of a Class

Expand Down
71 changes: 60 additions & 11 deletions repl/src/dotty/tools/repl/ReplCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import dotc.core.Contexts.*
import dotc.core.CompilationUnitInfo
import dotc.core.Decorators.*
import dotc.core.Flags.*
import dotc.core.NameKinds.SimpleNameKind
import dotc.core.Names.*
import dotc.core.NameKinds.ReplAssignName
import dotc.core.Phases.Phase
import dotc.core.Phases.{Phase, checkCapturesPhase}
import dotc.core.Contexts.atPhase
import dotc.config.Feature
import dotc.core.StdNames.*
import dotc.core.Symbols.*
import dotc.reporting.Diagnostic
Expand Down Expand Up @@ -97,17 +100,63 @@ class ReplCompiler extends Compiler:
end compile

final def typeOf(expr: String)(using state: State): Either[List[Diagnostic], String] =
typeCheck(expr).map { (_, tpdTree) =>
given Context = state.context
tpdTree.rhs match {
case Block(xs, _) => xs.last.tpe.widen.show
case _ =>
"""Couldn't compute the type of your expression, so sorry :(
|
|Please report this to my masters at github.com/lampepfl/dotty
""".stripMargin
if Feature.ccEnabledSomewhere(using state.context) && checkCapturesPhase(using state.context).exists then
typeOfWithCC(expr)
else
typeCheck(expr).map { (_, tpdTree) =>
given Context = state.context
tpdTree.rhs match {
case Block(xs, _) => xs.last.tpe.widen.show
case _ =>
"""Couldn't compute the type of your expression, so sorry :(
|
|Please report this to my masters at github.com/lampepfl/dotty
""".stripMargin
}
}
}

/** Compute the type of `expr` using the full compilation pipeline,
* until the capture checking phases. This ensures that the
* displayed type reflects capture annotations.
*/
private def typeOfWithCC(expr: String)(using state: State): Either[List[Diagnostic], String] =
val src = SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), expr)
ParseResult(src) match
case parsed: Parsed =>
// Stop after CC — we only need types, not bytecode.
val ccCtx = state.context.fresh
.setSource(parsed.source)
.setSetting(state.context.settings.YstopAfter, List("cc"))
val compileState = state.copy(context = ccCtx)
compile(parsed)(using compileState).fold(
(errs, _) => Left(errs),
(unit, newState) =>
given Context = newState.context
atPhase(checkCapturesPhase) {
// Find the result val in the wrapper module
val wrapperName = (str.REPL_SESSION_LINE + newState.objectIndex).toTermName
val wrapperSym = defn.RootClass.info.member(nme.EMPTY_PACKAGE).symbol
.info.member(wrapperName).symbol
if wrapperSym.exists then
val fields = wrapperSym.info.fields
.filterNot(_.symbol.isOneOf(ParamAccessor | Private | Synthetic | Artifact | Module))
.filter(_.symbol.name.is(SimpleNameKind))
fields.lastOption match
case Some(field) => Right(field.symbol.info.widen.show)
case None =>
Left(List(new Diagnostic.Error(
s"Couldn't compute the type of your expression",
src.atSpan(Span(0, expr.length)))))
else
Left(List(new Diagnostic.Error(
s"Couldn't compute the type of your expression",
src.atSpan(Span(0, expr.length)))))
}
)
case SyntaxErrors(_, errs, _) => Left(errs)
case _ => Left(List(new Diagnostic.Error(
s"Couldn't parse '$expr' to valid scala",
src.atSpan(Span(0, expr.length)))))

def docOf(expr: String)(using state: State): Either[List[Diagnostic], String] = inContext(state.context) {

Expand Down
24 changes: 15 additions & 9 deletions repl/src/dotty/tools/repl/ReplDriver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dotty.tools
package repl

import scala.language.unsafeNulls
import scala.util.control.NonFatal

import java.io.{File => JFile, PrintStream}
import java.nio.charset.StandardCharsets
Expand Down Expand Up @@ -45,7 +46,6 @@ import scala.collection.mutable
import scala.compiletime.uninitialized
import scala.jdk.CollectionConverters.*
import scala.tools.asm.ClassReader
import scala.util.control.NonFatal
import scala.util.Using

/** The state of the REPL contains necessary bindings instead of having to have
Expand Down Expand Up @@ -650,21 +650,27 @@ class ReplDriver(settings: Array[String],
expr match {
case "" => out.println(s":type <expression>")
case _ =>
compiler.typeOf(expr)(using newRun(state)).fold(
errs => displayErrors(errs, state),
res => out.println(res) // result has some highlights
)
try
compiler.typeOf(expr)(using newRun(state)).fold(
errs => displayErrors(errs, state),
res => out.println(res) // result has some highlights
)
catch case NonFatal(ex) =>
out.println(s"Error: ${ex.getMessage}")
}
state

case DocOf(expr) =>
expr match {
case "" => out.println(s":doc <expression>")
case _ =>
compiler.docOf(expr)(using newRun(state)).fold(
errs => displayErrors(errs, state),
res => out.println(res)
)
try
compiler.docOf(expr)(using newRun(state)).fold(
errs => displayErrors(errs, state),
res => out.println(res)
)
catch case NonFatal(ex) =>
out.println(s"Error: ${ex.getMessage}")
}
state

Expand Down
Loading
Loading