@@ -90,20 +90,51 @@ are methods of the `Type` class, so call them with `this` as a receiver:
90
90
91
91
- ` isNullableUnion ` determines whether ` this ` is a nullable union.
92
92
- ` isJavaNullableUnion ` determines whether ` this ` is syntactically a union of the form
93
- ` T|JavaNull `
93
+ ` T|JavaNull ` .
94
94
- ` stripNull ` syntactically strips all ` Null ` types in the union:
95
95
e.g. ` String|Null => String ` .
96
96
- ` stripAllJavaNull ` is like ` stripNull ` but only removes ` JavaNull ` from the union.
97
97
This is needed when we want to "revert" the Java nullification function.
98
98
99
99
## Flow Typing
100
100
101
- ` NotNullInfo ` s are collected as we typing each statements, see ` Nullables.scala ` for more
102
- details about how we compute ` NotNullInfo ` s.
101
+ As typing happens, we accumulate a set of ` NotNullInfo ` s in the ` Context ` (see
102
+ ` Contexts.scala ` ). A ` NotNullInfo ` contains the set of ` TermRef ` s that are known to
103
+ be non-null at the current program point. See ` Nullables.scala ` for how ` NotNullInfo ` s
104
+ are computed.
103
105
104
- When we type an identity or a select tree (in ` typedIdent ` and ` typedSelect ` ), we will
105
- call ` toNotNullTermRef ` on the tree before reture the result. If the tree ` x ` has nullable
106
- type ` T|Null ` and it is known to be not null according to the ` NotNullInfo ` and it is not
107
- on the lhs of assignment, then we cast it to ` x.type & T ` using ` defn.Any_typeCast ` . The
108
- reason to have a ` TermRef(x) ` in the ` AndType ` is that we can track the new result as well and
109
- use it as a path.
106
+ During type-checking, when we type an identity or a select tree (in ` typedIdent ` and
107
+ ` typedSelect ` ), we will call ` toNotNullTermRef ` on the tree before return the typed tree.
108
+ If the tree ` x ` has nullable type ` T|Null ` and it is known to be not null according to
109
+ the ` NotNullInfo ` and it is not on the lhs of assignment, then we cast it to ` x.type & T `
110
+ using ` defn.Any_typeCast ` .
111
+
112
+ The reason for casting to ` x.type & T ` , as opposed to just ` T ` , is that it allows us to
113
+ support flow typing for paths of length greater than one.
114
+
115
+ ``` scala
116
+ abstract class Node {
117
+ val x : String
118
+ val next : Node | Null
119
+ }
120
+
121
+ def f = {
122
+ val l : Node | Null = ???
123
+ if (l != null && l.next != null ) {
124
+ val third : l.next.next.type = l.next.next
125
+ }
126
+ }
127
+ ```
128
+
129
+ After typing, ` f ` becomes:
130
+
131
+ ``` scala
132
+ def f = {
133
+ val l : Node | Null = ???
134
+ if (l != null && l.$asInstanceOf $[l.type & Node ].next != null ) {
135
+ val third :
136
+ l.$asInstanceOf $[l.type & Node ].next.$asInstanceOf $[(l.type & Node ).next.type & Node ].next.type =
137
+ l.$asInstanceOf $[l.type & Node ].next.$asInstanceOf $[(l.type & Node ).next.type & Node ].next
138
+ }
139
+ }
140
+ ```
0 commit comments