1- package dotty .tools .dotc .core
1+ package dotty .tools .dotc
2+ package core
23
3- import dotty .tools .dotc .core .Contexts ._
4- import dotty .tools .dotc .core .Flags .JavaDefined
5- import dotty .tools .dotc .core .StdNames .{jnme , nme }
6- import dotty .tools .dotc .core .Symbols ._
7- import dotty .tools .dotc .core .Types ._
4+ import config .Feature ._
5+ import Contexts ._
6+ import Flags .JavaDefined
87import NullOpsDecorator ._
8+ import StdNames .nme
9+ import Symbols ._
10+ import Types ._
911
1012/** This module defines methods to interpret types of Java symbols, which are implicitly nullable in Java,
1113 * as Scala types, which are explicitly nullable.
1214 *
1315 * The transformation is (conceptually) a function `n` that adheres to the following rules:
14- * (1) n(T) = T|UncheckedNull if T is a reference type
16+ * (1) n(T) = T | Null if T is a reference type
1517 * (2) n(T) = T if T is a value type
16- * (3) n(C[T]) = C[T]|UncheckedNull if C is Java-defined
17- * (4) n(C[T]) = C[n(T)]|UncheckedNull if C is Scala-defined
18- * (5) n(A|B) = n(A)| n(B)|UncheckedNull
18+ * (3) n(C[T]) = C[T] | Null if C is Java-defined
19+ * (4) n(C[T]) = C[n(T)] | Null if C is Scala-defined
20+ * (5) n(A|B) = n(A) | n(B) | Null
1921 * (6) n(A&B) = n(A) & n(B)
2022 * (7) n((A1, ..., Am)R) = (n(A1), ..., n(Am))n(R) for a method with arguments (A1, ..., Am) and return type R
2123 * (8) n(T) = T otherwise
2224 *
2325 * Treatment of generics (rules 3 and 4):
24- * - if `C` is Java-defined, then `n(C[T]) = C[T]|UncheckedNull `. That is, we don't recurse
25- * on the type argument, and only add UncheckedNull on the outside. This is because
26+ * - if `C` is Java-defined, then `n(C[T]) = C[T] | Null `. That is, we don't recurse
27+ * on the type argument, and only add Null on the outside. This is because
2628 * `C` itself will be nullified, and in particular so will be usages of `C`'s type argument within C's body.
2729 * e.g. calling `get` on a `java.util.List[String]` already returns `String|Null` and not `String`, so
28- * we don't need to write `java.util.List[String| Null]`.
29- * - if `C` is Scala-defined, however, then we want `n(C[T]) = C[n(T)]|UncheckedNull `. This is because
30+ * we don't need to write `java.util.List[String | Null]`.
31+ * - if `C` is Scala-defined, however, then we want `n(C[T]) = C[n(T)] | Null `. This is because
3032 * `C` won't be nullified, so we need to indicate that its type argument is nullable.
3133 *
3234 * Notice that since the transformation is only applied to types attached to Java symbols, it doesn't need
@@ -43,10 +45,9 @@ object JavaNullInterop {
4345 *
4446 * After calling `nullifyMember`, Scala will see the method as
4547 *
46- * def foo(arg: String|UncheckedNull ): String|UncheckedNull
48+ * def foo(arg: String | Null ): String | Null
4749 *
48- * This nullability function uses `UncheckedNull` instead of vanilla `Null`, for usability.
49- * This means that we can select on the return of `foo`:
50+ * If unsafeNulls is enabled, we can select on the return of `foo`:
5051 *
5152 * val len = foo("hello").length
5253 *
@@ -57,10 +58,10 @@ object JavaNullInterop {
5758 assert(sym.is(JavaDefined ), " can only nullify java-defined members" )
5859
5960 // Some special cases when nullifying the type
60- if ( isEnumValueDef || sym.name == nme.TYPE_ )
61+ if isEnumValueDef || sym.name == nme.TYPE_ then
6162 // Don't nullify the `TYPE` field in every class and Java enum instances
6263 tp
63- else if ( sym.name == nme.toString_ || sym.isConstructor || hasNotNullAnnot(sym))
64+ else if sym.name == nme.toString_ || sym.isConstructor || hasNotNullAnnot(sym) then
6465 // Don't nullify the return type of the `toString` method.
6566 // Don't nullify the return type of constructors.
6667 // Don't nullify the return type of methods with a not-null annotation.
@@ -81,20 +82,20 @@ object JavaNullInterop {
8182 private def nullifyExceptReturnType (tp : Type )(using Context ): Type =
8283 new JavaNullMap (true )(tp)
8384
84- /** Nullifies a Java type by adding `| UncheckedNull ` in the relevant places. */
85+ /** Nullifies a Java type by adding `| Null ` in the relevant places. */
8586 private def nullifyType (tp : Type )(using Context ): Type =
8687 new JavaNullMap (false )(tp)
8788
88- /** A type map that implements the nullification function on types. Given a Java-sourced type, this adds `| UncheckedNull `
89+ /** A type map that implements the nullification function on types. Given a Java-sourced type, this adds `| Null `
8990 * in the right places to make the nulls explicit in Scala.
9091 *
9192 * @param outermostLevelAlreadyNullable whether this type is already nullable at the outermost level.
92- * For example, `Array[String]|UncheckedNull ` is already nullable at the
93- * outermost level, but `Array[String|UncheckedNull ]` isn't.
93+ * For example, `Array[String] | Null ` is already nullable at the
94+ * outermost level, but `Array[String | Null ]` isn't.
9495 * If this parameter is set to true, then the types of fields, and the return
9596 * types of methods will not be nullified.
9697 * This is useful for e.g. constructors, and also so that `A & B` is nullified
97- * to `(A & B) | UncheckedNull `, instead of `(A|UncheckedNull & B|UncheckedNull ) | UncheckedNull `.
98+ * to `(A & B) | Null `, instead of `(A | Null & B | Null ) | Null `.
9899 */
99100 private class JavaNullMap (var outermostLevelAlreadyNullable : Boolean )(using Context ) extends TypeMap {
100101 /** Should we nullify `tp` at the outermost level? */
@@ -107,15 +108,15 @@ object JavaNullInterop {
107108 ! tp.isRef(defn.AnyClass ) &&
108109 // We don't nullify Java varargs at the top level.
109110 // Example: if `setNames` is a Java method with signature `void setNames(String... names)`,
110- // then its Scala signature will be `def setNames(names: (String|UncheckedNull )*): Unit`.
111+ // then its Scala signature will be `def setNames(names: (String|Null )*): Unit`.
111112 // This is because `setNames(null)` passes as argument a single-element array containing the value `null`,
112113 // and not a `null` array.
113114 ! tp.isRef(defn.RepeatedParamClass )
114115 case _ => true
115116 })
116117
117118 override def apply (tp : Type ): Type = tp match {
118- case tp : TypeRef if needsNull(tp) => OrUncheckedNull (tp)
119+ case tp : TypeRef if needsNull(tp) => OrNull (tp)
119120 case appTp @ AppliedType (tycon, targs) =>
120121 val oldOutermostNullable = outermostLevelAlreadyNullable
121122 // We don't make the outmost levels of type arguments nullable if tycon is Java-defined.
@@ -125,7 +126,7 @@ object JavaNullInterop {
125126 val targs2 = targs map this
126127 outermostLevelAlreadyNullable = oldOutermostNullable
127128 val appTp2 = derivedAppliedType(appTp, tycon, targs2)
128- if ( needsNull(tycon)) OrUncheckedNull (appTp2) else appTp2
129+ if needsNull(tycon) then OrNull (appTp2) else appTp2
129130 case ptp : PolyType =>
130131 derivedLambdaType(ptp)(ptp.paramInfos, this (ptp.resType))
131132 case mtp : MethodType =>
@@ -136,11 +137,11 @@ object JavaNullInterop {
136137 derivedLambdaType(mtp)(paramInfos2, this (mtp.resType))
137138 case tp : TypeAlias => mapOver(tp)
138139 case tp : AndType =>
139- // nullify(A & B) = (nullify(A) & nullify(B)) | UncheckedNull , but take care not to add
140- // duplicate `UncheckedNull `s at the outermost level inside `A` and `B`.
140+ // nullify(A & B) = (nullify(A) & nullify(B)) | Null , but take care not to add
141+ // duplicate `Null `s at the outermost level inside `A` and `B`.
141142 outermostLevelAlreadyNullable = true
142- OrUncheckedNull (derivedAndType(tp, this (tp.tp1), this (tp.tp2)))
143- case tp : TypeParamRef if needsNull(tp) => OrUncheckedNull (tp)
143+ OrNull (derivedAndType(tp, this (tp.tp1), this (tp.tp2)))
144+ case tp : TypeParamRef if needsNull(tp) => OrNull (tp)
144145 // In all other cases, return the type unchanged.
145146 // In particular, if the type is a ConstantType, then we don't nullify it because it is the
146147 // type of a final non-nullable field.
0 commit comments