Skip to content

Backport to fix a REPL bad symbolic reference #19756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
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
19 changes: 15 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,28 @@ object Symbols {
}

private def computeDenot(lastd: SymDenotation)(using Context): SymDenotation = {
// Written that way so that it comes in at 32 bytes and is therefore inlineable for
// the JIT (reputedly, cutoff is at 35 bytes)
util.Stats.record("Symbol.computeDenot")
val now = ctx.period
checkedPeriod = now
if (lastd.validFor contains now) lastd else recomputeDenot(lastd)
if lastd.validFor.contains(now) then lastd else recomputeDenot(lastd)
}

/** Overridden in NoSymbol */
protected def recomputeDenot(lastd: SymDenotation)(using Context): SymDenotation = {
util.Stats.record("Symbol.recomputeDenot")
val newd = lastd.current.asInstanceOf[SymDenotation]
lastDenot = newd
if newd.exists || lastd.initial.validFor.firstPhaseId <= ctx.phaseId then
lastDenot = newd
else
// We are trying to bring forward a symbol that is defined only at a later phase
// (typically, a nested Java class, invisible before erasure).
// In that case, keep lastDenot as it was and set the checked period to lastDenot's
// previous validity, which means we will try another bring forward when the symbol
// is referenced at a later phase. Otherwise we'd get stuck on NoDenotation here.
// See #15562 and test i15562b in ReplCompilerTests
checkedPeriod = lastd.validFor
newd
}

Expand Down Expand Up @@ -754,7 +765,7 @@ object Symbols {
cls: ClassSymbol,
name: TermName = nme.WILDCARD,
selfInfo: Type = NoType)(using Context): TermSymbol =
newSymbol(cls, name, SelfSymFlags, selfInfo orElse cls.classInfo.selfType, coord = cls.coord)
newSymbol(cls, name, SelfSymFlags, selfInfo.orElse(cls.classInfo.selfType), coord = cls.coord)

/** Create new type parameters with given owner, names, and flags.
* @param boundsFn A function that, given type refs to the newly created
Expand Down Expand Up @@ -921,7 +932,7 @@ object Symbols {
*/
def getPackageClassIfDefined(path: PreName)(using Context): Symbol =
staticRef(path.toTypeName, isPackage = true, generateStubs = false)
.disambiguate(_ is PackageClass).symbol
.disambiguate(_.is(PackageClass)).symbol

def requiredModule(path: PreName)(using Context): TermSymbol = {
val name = path.toTermName
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/report.scala
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ object report:
| An unhandled exception was thrown in the compiler.
| Please file a crash report here:
| https://github.com/lampepfl/dotty/issues/new/choose
| For non-enriched exceptions, compile with -Yno-enrich-error-messages.
|
|$info1
|""".stripMargin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ trait HideNonSensicalMessages extends Reporter {
*/
override def isHidden(dia: Diagnostic)(using Context): Boolean =
super.isHidden(dia) || {
dia.msg.isNonSensical &&
hasErrors && // if there are no errors yet, report even if diagnostic is non-sensical
!ctx.settings.YshowSuppressedErrors.value
hasErrors // if there are no errors yet, report even if diagnostic is non-sensical
&& dia.msg.isNonSensical // defer forcing the message by calling hasErrors first
&& !ctx.settings.YshowSuppressedErrors.value
}
}
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/reporting/Reporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ abstract class Reporter extends interfaces.ReporterResult {
case w: Warning if ctx.settings.XfatalWarnings.value => w.toError
case _ => dia
if !isHidden(d) then // avoid isHidden test for summarized warnings so that message is not forced
markReported(d)
withMode(Mode.Printing)(doReport(d))
d match {
case _: Warning => _warningCount += 1
case e: Error =>
Expand All @@ -166,6 +164,8 @@ abstract class Reporter extends interfaces.ReporterResult {
case _: Info => // nothing to do here
// match error if d is something else
}
markReported(dia)
withMode(Mode.Printing)(doReport(dia))
end issueUnconfigured

def issueIfNotSuppressed(dia: Diagnostic)(using Context): Unit =
Expand Down
28 changes: 28 additions & 0 deletions compiler/test/dotty/tools/repl/ReplCompilerTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,34 @@ class ReplCompilerTests extends ReplTest:
@Test def `i13097 expect template after colon` = contextually:
assert(ParseResult.isIncomplete("class C:"))

@Test def i15562: Unit = initially {
val s1 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
assertEquals("2", storedOutput().trim)
s1
} andThen { s1 ?=>
val comp = tabComplete("List(1, 2).filter(_ % 2 == 0).fore")
assertEquals(List("foreach"), comp.distinct)
s1
} andThen {
val s2 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
assertEquals("2", storedOutput().trim)
s2
}

@Test def i15562b: Unit = initially {
val s1 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
assertEquals("2", storedOutput().trim)
s1
} andThen { s1 ?=>
val comp = tabComplete("val x = false + true; List(1, 2).filter(_ % 2 == 0).fore")
assertEquals(List("foreach"), comp.distinct)
s1
} andThen {
val s2 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
assertEquals("2", storedOutput().trim)
s2
}

object ReplCompilerTests:

private val pattern = Pattern.compile("\\r[\\n]?|\\n");
Expand Down
4 changes: 4 additions & 0 deletions compiler/test/dotty/tools/repl/ReplTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na

def contextually[A](op: Context ?=> A): A = op(using initialState.context)

/** Returns the `(<instance completions>, <companion completions>)`*/
def tabComplete(src: String)(implicit state: State): List[String] =
completions(src.length, src, state).map(_.value).sorted

extension [A](state: State)
infix def andThen(op: State ?=> A): A = op(using state)

Expand Down
4 changes: 0 additions & 4 deletions compiler/test/dotty/tools/repl/TabcompleteTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import org.junit.Test
/** These tests test input that has proved problematic */
class TabcompleteTests extends ReplTest {

/** Returns the `(<instance completions>, <companion completions>)`*/
private def tabComplete(src: String)(implicit state: State): List[String] =
completions(src.length, src, state).map(_.value).sorted

@Test def tabCompleteList = initially {
val comp = tabComplete("List.r")
assertEquals(List("range"), comp.distinct)
Expand Down