Skip to content

Commit 00e988e

Browse files
committed
Add nullTrackable annotation to force tracking mutale fields
1 parent 57db77a commit 00e988e

File tree

4 files changed

+30
-5
lines changed

4 files changed

+30
-5
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,7 @@ class Definitions {
10951095
@tu lazy val RetainsByNameAnnot: ClassSymbol = requiredClass("scala.annotation.retainsByName")
10961096
@tu lazy val PublicInBinaryAnnot: ClassSymbol = requiredClass("scala.annotation.publicInBinary")
10971097
@tu lazy val WitnessNamesAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WitnessNames")
1098+
@tu lazy val NullTrackableAnnot: ClassSymbol = requiredClass("scala.annotation.nullTrackable")
10981099

10991100
@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")
11001101

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,22 @@ object Nullables:
186186
* Check `usedOutOfOrder` to see the explaination and example of "out of order".
187187
* See more examples in `tests/explicit-nulls/neg/var-ref-in-closure.scala`.
188188
*/
189-
def isTracked(ref: TermRef)(using Context) =
189+
def isTracked(ref: TermRef)(using Context) = // true
190+
val sym = ref.symbol
191+
192+
def isNullTrackableField: Boolean =
193+
ref.prefix.isStable
194+
&& sym.isField
195+
&& sym.hasAnnotation(defn.NullTrackableAnnot)
196+
197+
// println(s"isTracked: $ref, usedOutOfOrder = ${ref.usedOutOfOrder}, isStable = ${ref.isStable}, span = ${ref.symbol.span}, assignmentSpans = ${ctx.compilationUnit.assignmentSpans.get(ref.symbol.span.start)}")
190198
ref.isStable
191-
|| { val sym = ref.symbol
192-
val unit = ctx.compilationUnit
199+
|| isNullTrackableField
200+
|| { val unit = ctx.compilationUnit
193201
!ref.usedOutOfOrder
194202
&& sym.span.exists
195203
&& (unit ne NoCompilationUnit) // could be null under -Ytest-pickler
196-
&& unit.assignmentSpans.contains(sym.span.start)
197-
}
204+
&& unit.assignmentSpans.contains(sym.span.start) }
198205

199206
/** The nullability context to be used after a case that matches pattern `pat`.
200207
* If `pat` is `null`, this will assert that the selector `sel` is not null afterwards.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scala.annotation
2+
3+
final class nullTrackable extends StaticAnnotation
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import scala.annotation.nullTrackable
2+
3+
class A:
4+
@nullTrackable var s: String | Null = null
5+
def getS: String =
6+
if s == null then s = ""
7+
s
8+
9+
def test(a: A): String =
10+
if a.s == null then
11+
a.s = ""
12+
a.s
13+
else
14+
a.s

0 commit comments

Comments
 (0)