Skip to content

Commit

Permalink
Fix a REPL bad symbolic reference (#19786)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand authored Feb 26, 2024
2 parents a015a15 + 46c50e7 commit b701542
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 8 deletions.
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 @@ -110,17 +110,28 @@ object Symbols extends SymUtils {
}

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 @@ -791,7 +802,7 @@ object Symbols extends SymUtils {
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 @@ -958,7 +969,7 @@ object Symbols extends SymUtils {
*/
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
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 @@ -409,6 +409,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

0 comments on commit b701542

Please sign in to comment.