Skip to content

Commit b701542

Browse files
authored
Fix a REPL bad symbolic reference (#19786)
2 parents a015a15 + 46c50e7 commit b701542

File tree

4 files changed

+47
-8
lines changed

4 files changed

+47
-8
lines changed

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,28 @@ object Symbols extends SymUtils {
110110
}
111111

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

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

@@ -791,7 +802,7 @@ object Symbols extends SymUtils {
791802
cls: ClassSymbol,
792803
name: TermName = nme.WILDCARD,
793804
selfInfo: Type = NoType)(using Context): TermSymbol =
794-
newSymbol(cls, name, SelfSymFlags, selfInfo orElse cls.classInfo.selfType, coord = cls.coord)
805+
newSymbol(cls, name, SelfSymFlags, selfInfo.orElse(cls.classInfo.selfType), coord = cls.coord)
795806

796807
/** Create new type parameters with given owner, names, and flags.
797808
* @param boundsFn A function that, given type refs to the newly created
@@ -958,7 +969,7 @@ object Symbols extends SymUtils {
958969
*/
959970
def getPackageClassIfDefined(path: PreName)(using Context): Symbol =
960971
staticRef(path.toTypeName, isPackage = true, generateStubs = false)
961-
.disambiguate(_ is PackageClass).symbol
972+
.disambiguate(_.is(PackageClass)).symbol
962973

963974
def requiredModule(path: PreName)(using Context): TermSymbol = {
964975
val name = path.toTermName

compiler/test/dotty/tools/repl/ReplCompilerTests.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,34 @@ class ReplCompilerTests extends ReplTest:
409409
@Test def `i13097 expect template after colon` = contextually:
410410
assert(ParseResult.isIncomplete("class C:"))
411411

412+
@Test def i15562: Unit = initially {
413+
val s1 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
414+
assertEquals("2", storedOutput().trim)
415+
s1
416+
} andThen { s1 ?=>
417+
val comp = tabComplete("List(1, 2).filter(_ % 2 == 0).fore")
418+
assertEquals(List("foreach"), comp.distinct)
419+
s1
420+
} andThen {
421+
val s2 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
422+
assertEquals("2", storedOutput().trim)
423+
s2
424+
}
425+
426+
@Test def i15562b: Unit = initially {
427+
val s1 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
428+
assertEquals("2", storedOutput().trim)
429+
s1
430+
} andThen { s1 ?=>
431+
val comp = tabComplete("val x = false + true; List(1, 2).filter(_ % 2 == 0).fore")
432+
assertEquals(List("foreach"), comp.distinct)
433+
s1
434+
} andThen {
435+
val s2 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)")
436+
assertEquals("2", storedOutput().trim)
437+
s2
438+
}
439+
412440
object ReplCompilerTests:
413441

414442
private val pattern = Pattern.compile("\\r[\\n]?|\\n");

compiler/test/dotty/tools/repl/ReplTest.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na
4040

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

43+
/** Returns the `(<instance completions>, <companion completions>)`*/
44+
def tabComplete(src: String)(implicit state: State): List[String] =
45+
completions(src.length, src, state).map(_.value).sorted
46+
4347
extension [A](state: State)
4448
infix def andThen(op: State ?=> A): A = op(using state)
4549

compiler/test/dotty/tools/repl/TabcompleteTests.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ import org.junit.Test
88
/** These tests test input that has proved problematic */
99
class TabcompleteTests extends ReplTest {
1010

11-
/** Returns the `(<instance completions>, <companion completions>)`*/
12-
private def tabComplete(src: String)(implicit state: State): List[String] =
13-
completions(src.length, src, state).map(_.value).sorted
14-
1511
@Test def tabCompleteList = initially {
1612
val comp = tabComplete("List.r")
1713
assertEquals(List("range"), comp.distinct)

0 commit comments

Comments
 (0)