Skip to content

Commit ce606b6

Browse files
committed
Fix closure check
1 parent 7d40d5c commit ce606b6

File tree

4 files changed

+100
-36
lines changed

4 files changed

+100
-36
lines changed

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -823,12 +823,9 @@ trait Implicits { self: Typer =>
823823
// if (x.asInstanceOf[String|Null] == null) {} // ok
824824
cls1 == defn.NullClass && cls1 == cls2
825825
else if (cls1 == defn.NullClass)
826-
cls1 == cls2
827-
else if (!ctx.explicitNulls)
828-
if (cls1 == defn.NullClass)
829-
cls2.derivesFrom(defn.ObjectClass)
830-
else
831-
cls2 == defn.NullClass && cls1.derivesFrom(defn.ObjectClass)
826+
cls1 == cls2 || cls2.derivesFrom(defn.ObjectClass)
827+
else if (cls2 == defn.NullClass)
828+
cls1.derivesFrom(defn.ObjectClass)
832829
else
833830
false
834831
}

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,10 @@ object Nullables with
110110
|| { val sym = ref.symbol
111111
sym.is(Mutable)
112112
&& sym.owner.isTerm
113-
&& !curCtx.owner.is(Flags.Lazy) // not a rhs of lazy ValDef
114-
&& sym.owner.enclosingMethod == curCtx.owner.enclosingMethod
113+
&& (if sym.owner != curCtx.owner then
114+
!curCtx.owner.is(Flags.Lazy) // not at the rhs of lazy ValDef
115+
&& sym.owner.enclosingMethod == curCtx.owner.enclosingMethod // not in different DefDef
116+
else true)
115117
&& sym.span.exists
116118
&& curCtx.compilationUnit != null // could be null under -Ytest-pickler
117119
&& curCtx.compilationUnit.assignmentSpans.contains(sym.span.start)
@@ -254,11 +256,16 @@ object Nullables with
254256

255257
given assignOps: (tree: Assign)
256258
def computeAssignNullable()(given Context): tree.type = tree.lhs match
257-
case TrackedRef(ref) => tree.rhs.typeOpt match
258-
// If the type of rhs is `T|Null`, then the nullability of the lhs variable is no longer
259-
// trackable. We don't need to check whether the type `T` is correct here.
260-
case OrNull(_) => tree.withNotNullInfo(NotNullInfo(Set(), Set(ref)))
261-
case _ => tree
259+
case TrackedRef(ref) =>
260+
def withoutRef: tree.type = tree.withNotNullInfo(NotNullInfo(Set(), Set(ref)))
261+
tree.rhs.typeOpt match
262+
// If the type of rhs is `T|Null`, then the nullability of the lhs variable is no longer
263+
// trackable. We don't need to check whether the type `T` is correct here, as typer will
264+
// check it.
265+
case OrNull(_) => withoutRef
266+
// If the type of rhs is Null, we discard its NotNullInfo.
267+
case tp if tp.isNullType => withoutRef
268+
case _ => tree
262269
case _ => tree
263270

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

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

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,19 @@ def f = {
1818

1919
def g = {
2020
var x: String|Null = ???
21-
lazy val y = {
22-
if (x != null) {
23-
x = null
24-
}
25-
26-
}
2721
if (x != null) {
28-
val a: String = x // error: x exists in closure, no longer tackable
22+
val a: String = x
23+
x = null
24+
val b: String = x // error: x is null
2925
}
3026
}
3127

3228
def h = {
33-
var x: String|Null = "???"
34-
lazy val y = {
35-
if (x != null) {
36-
val a: String = x // error: x exists in closure, no longer tackable
37-
}
38-
x
39-
}
40-
}
41-
42-
def i = {
43-
var x: String|Null = "???"
44-
def y = {
45-
if (x != null) {
46-
val a: String = x // error: x exists in closure, no longer tackable
47-
}
48-
x
29+
var x: String|Null = ???
30+
if (x != null) {
31+
val a: String = x
32+
val b: String | String = a
33+
x = b
34+
val _: String = x // ok
4935
}
5036
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Test that we don't track variables which is refered in another closure.
2+
3+
object VarRef {
4+
locally {
5+
var x: String|Null = ???
6+
val y = {
7+
if (x != null) {
8+
val _: String = x // ok: y doesn't create closure
9+
}
10+
}
11+
if (x != null) {
12+
val a: String = x // ok
13+
}
14+
}
15+
16+
locally {
17+
var x: String|Null = ???
18+
lazy val y = {
19+
if (x != null) {
20+
x = null
21+
}
22+
x
23+
}
24+
if (x != null) {
25+
val a: String = x // error: x exists in closure, no longer tackable
26+
}
27+
}
28+
29+
locally {
30+
var x: String|Null = ???
31+
def y = {
32+
if (x != null) {
33+
x = null
34+
}
35+
x
36+
}
37+
if (x != null) {
38+
val a: String = x // error: x exists in closure, no longer tackable
39+
}
40+
}
41+
42+
43+
locally {
44+
var x: String|Null = "???"
45+
lazy val y = {
46+
if (x != null) {
47+
val a: String = x // error: x exists in closure, no longer tackable
48+
}
49+
x
50+
}
51+
}
52+
53+
locally {
54+
var x: String|Null = "???"
55+
def y = {
56+
if (x != null) {
57+
val a: String = x // error: x exists in closure, no longer tackable
58+
}
59+
x
60+
}
61+
}
62+
63+
lazy val lazyblock = {
64+
var x: String|Null = "???"
65+
lazy val y = {
66+
if (x != null) {
67+
// The enclosingMethods of x definition and x reference hare are same
68+
val a: String = x // error: x exists in closure, no longer tackable
69+
}
70+
x
71+
}
72+
}
73+
}
74+

0 commit comments

Comments
 (0)