Skip to content

Commit

Permalink
Fix #19202: Passing latest NotNullInfos to a mutable field of a Compl…
Browse files Browse the repository at this point in the history
…eter (#19463)

Fix #19202 by passing latest `NotNullInfos` to a mutable field of a `Completer`.

Completing a `Def` requires the new NotNullInfos from previous
statements.
Originally, we created a new Completer with the new creation ctx in
`adaptCreationContext`.
However, we should not create a regular Completer for `TypeDef`s.

This PR fixes the issue by creating a mutable field for NotNullInfos in
Completer,
and the field is updated  in `adaptCreationContext` now.
  • Loading branch information
noti0na1 committed Jan 19, 2024
2 parents eb41221 + 44e63a1 commit 0b7a785
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 4 deletions.
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import parsing.JavaParsers.JavaParser
import parsing.Parsers.Parser
import Annotations.*
import Inferencing.*
import Nullables.*
import transform.ValueClasses.*
import TypeErasure.erasure
import reporting.*
Expand Down Expand Up @@ -784,12 +785,19 @@ class Namer { typer: Typer =>

protected def localContext(owner: Symbol): FreshContext = ctx.fresh.setOwner(owner).setTree(original)

/** Stores the latest NotNullInfos (updated by `setNotNullInfos`) */
private var myNotNullInfos: List[NotNullInfo] | Null = null

/** The context with which this completer was created */
given creationContext: Context = ictx
given creationContext[Dummy_so_its_a_def]: Context =
if myNotNullInfos == null then ictx else ictx.withNotNullInfos(myNotNullInfos.nn)

// make sure testing contexts are not captured by completers
assert(!ictx.reporter.isInstanceOf[ExploringReporter])

def setNotNullInfos(infos: List[NotNullInfo]): Unit =
myNotNullInfos = infos

protected def typeSig(sym: Symbol): Type = original match
case original: ValDef =>
if (sym.is(Module)) moduleValSig(sym)
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3361,12 +3361,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
mdef.getAttachment(SymOfTree) match {
case Some(sym) => sym.infoOrCompleter match {
case completer: Namer#Completer =>
if (completer.creationContext.notNullInfos ne ctx.notNullInfos)
if completer.creationContext.notNullInfos ne ctx.notNullInfos then
// The RHS of a val def should know about not null facts established
// in preceding statements (unless the DefTree is completed ahead of time,
// then it is impossible).
sym.info = Completer(completer.original)(
completer.creationContext.withNotNullInfos(ctx.notNullInfos))
completer.setNotNullInfos(ctx.notNullInfos)
true
case _ =>
// If it has been completed, then it must be because there is a forward reference
Expand Down
27 changes: 27 additions & 0 deletions tests/explicit-nulls/pos/i19202.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Test {
def test1(s: String | Null): Unit = {
if s == null then return

case class XXX()
}

def test2(s: String | Null): Unit = {
if s == "" then return

case class XXX()
}

def test3(s: String | Null): Unit = {
if s == null then return

case class XXX()
()
}

def test4(s: String | Null): String | Null = {
if s == null then return ""

case class XXX()
"xxx"
}
}
27 changes: 27 additions & 0 deletions tests/pos/i19202.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Test {
def test1(s: String): Unit = {
if s == null then return

case class XXX()
}

def test2(s: String): Unit = {
if s == "" then return

case class XXX()
}

def test3(s: String): Unit = {
if s == null then return

case class XXX()
()
}

def test4(s: String): String = {
if s == null then return ""

case class XXX()
"xxx"
}
}

0 comments on commit 0b7a785

Please sign in to comment.