Skip to content

Commit 4f53b9b

Browse files
noti0na1tgodzik
authored andcommitted
Fix deep NotNullInfo
1 parent 252f4e3 commit 4f53b9b

File tree

6 files changed

+59
-22
lines changed

6 files changed

+59
-22
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ trait Applications extends Compatibility {
10581058
case _ => ()
10591059
else ()
10601060

1061-
fun1.tpe match {
1061+
val result = fun1.tpe match {
10621062
case err: ErrorType => cpy.Apply(tree)(fun1, proto.typedArgs()).withType(err)
10631063
case TryDynamicCallType =>
10641064
val isInsertedApply = fun1 match {
@@ -1132,6 +1132,11 @@ trait Applications extends Compatibility {
11321132
else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
11331133
}
11341134
}
1135+
1136+
if result.tpe.isNothingType then
1137+
val nnInfo = result.notNullInfo
1138+
result.withNotNullInfo(nnInfo.terminatedInfo)
1139+
else result
11351140
}
11361141

11371142
/** Convert expression like

compiler/src/dotty/tools/dotc/typer/Nullables.scala

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,29 @@ object Nullables:
294294
if !info.isEmpty then tree.putAttachment(NNInfo, info)
295295
tree
296296

297+
/* Collect the nullability info from parts of `tree` */
298+
def collectNotNullInfo(using Context): NotNullInfo = tree match
299+
case Typed(expr, _) =>
300+
expr.notNullInfo
301+
case Apply(fn, args) =>
302+
val argsInfo = args.map(_.notNullInfo)
303+
val fnInfo = fn.notNullInfo
304+
argsInfo.foldLeft(fnInfo)(_ seq _)
305+
case TypeApply(fn, _) =>
306+
fn.notNullInfo
307+
case _ =>
308+
// Other cases are handled specially in typer.
309+
NotNullInfo.empty
310+
297311
/* The nullability info of `tree` */
298312
def notNullInfo(using Context): NotNullInfo =
299-
stripInlined(tree).getAttachment(NNInfo) match
313+
val tree1 = stripInlined(tree)
314+
tree1.getAttachment(NNInfo) match
300315
case Some(info) if !ctx.erasedTypes => info
301-
case _ => NotNullInfo.empty
316+
case _ =>
317+
val nnInfo = tree1.collectNotNullInfo
318+
tree1.withNotNullInfo(nnInfo)
319+
nnInfo
302320

303321
/* The nullability info of `tree`, assuming it is a condition that evaluates to `c` */
304322
def notNullInfoIf(c: Boolean)(using Context): NotNullInfo =
@@ -379,21 +397,23 @@ object Nullables:
379397
end extension
380398

381399
extension (tree: Assign)
382-
def computeAssignNullable()(using Context): tree.type = tree.lhs match
383-
case TrackedRef(ref) =>
384-
val rhstp = tree.rhs.typeOpt
385-
if ctx.explicitNulls && ref.isNullableUnion then
386-
if rhstp.isNullType || rhstp.isNullableUnion then
387-
// If the type of rhs is nullable (`T|Null` or `Null`), then the nullability of the
388-
// lhs variable is no longer trackable. We don't need to check whether the type `T`
389-
// is correct here, as typer will check it.
390-
tree.withNotNullInfo(NotNullInfo(Set(), Set(ref)))
391-
else
392-
// If the initial type is nullable and the assigned value is non-null,
393-
// we add it to the NotNull.
394-
tree.withNotNullInfo(NotNullInfo(Set(ref), Set()))
395-
else tree
396-
case _ => tree
400+
def computeAssignNullable()(using Context): tree.type =
401+
var nnInfo = tree.rhs.notNullInfo
402+
tree.lhs match
403+
case TrackedRef(ref) if ctx.explicitNulls && ref.isNullableUnion =>
404+
nnInfo = nnInfo.seq:
405+
val rhstp = tree.rhs.typeOpt
406+
if rhstp.isNullType || rhstp.isNullableUnion then
407+
// If the type of rhs is nullable (`T|Null` or `Null`), then the nullability of the
408+
// lhs variable is no longer trackable. We don't need to check whether the type `T`
409+
// is correct here, as typer will check it.
410+
NotNullInfo(Set(), Set(ref))
411+
else
412+
// If the initial type is nullable and the assigned value is non-null,
413+
// we add it to the NotNull.
414+
NotNullInfo(Set(ref), Set())
415+
case _ =>
416+
tree.withNotNullInfo(nnInfo)
397417
end extension
398418

399419
private val analyzedOps = Set(nme.EQ, nme.NE, nme.eq, nme.ne, nme.ZAND, nme.ZOR, nme.UNARY_!)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
10001000
untpd.unsplice(tree.expr).putAttachment(AscribedToUnit, ())
10011001
typed(tree.expr, underlyingTreeTpe.tpe.widenSkolem)
10021002
assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe)
1003-
.withNotNullInfo(expr1.notNullInfo)
10041003
}
10051004

10061005
if (untpd.isWildcardStarArg(tree)) {

tests/explicit-nulls/neg/flow-early-exit.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Foo(x: String|Null) {
4949
def retTypeNothing(): String = {
5050
val y: String|Null = ???
5151
if (y == null) err("y is null!")
52-
y // error
52+
y
5353
}
5454

5555
def errRetUnit(msg: String): Unit = {

tests/explicit-nulls/neg/flow-simple-var.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class SimpleVar {
1919
}
2020

2121
assert(x != null)
22-
val a: String = x // error
22+
val a: String = x
2323
x = nullable(x)
2424
val b: String = x // error: x might be null
2525
}

tests/explicit-nulls/neg/i21619.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,17 @@ def test5: Unit =
7676
catch
7777
case _ =>
7878
val z1: String = x.replace("", "") // error
79-
val z2: String = y.replace("", "") // error // LTS specific
79+
val z2: String = y.replace("", "") // error // LTS specific
80+
81+
def test6 = {
82+
var x: String | Null = ""
83+
var y: String = ""
84+
x = ""
85+
y = if (false) x else 1 match { // error
86+
case _ => {
87+
x = null
88+
y
89+
}
90+
}
91+
x.replace("", "") // error
92+
}

0 commit comments

Comments
 (0)