Skip to content
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

Fix a REPL bad symbolic reference #19786

Merged
merged 6 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -110,17 +110,28 @@ object Symbols extends SymUtils {
}

private def computeDenot(lastd: SymDenotation)(using Context): SymDenotation = {
// Written that way do that it comes in at 32 bytes and is therefore inlineable for
odersky marked this conversation as resolved.
Show resolved Hide resolved
// 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
Loading