Skip to content

Commit 48e6d8a

Browse files
committed
Rename stripNull
1 parent 76ddfae commit 48e6d8a

File tree

9 files changed

+54
-26
lines changed

9 files changed

+54
-26
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,7 @@ class Definitions {
613613
@tu lazy val StringModule: Symbol = StringClass.linkedClass
614614
@tu lazy val String_+ : TermSymbol = enterMethod(StringClass, nme.raw.PLUS, methOfAny(StringType), Final)
615615
@tu lazy val String_valueOf_Object: Symbol = StringModule.info.member(nme.valueOf).suchThat(_.info.firstParamTypes match {
616-
case List(pt) =>
617-
pt.isAny || pt.stripNull.isAnyRef
616+
case List(pt) => pt.isAny || pt.stripNullWhenExplicit.isAnyRef
618617
case _ => false
619618
}).symbol
620619

@@ -626,13 +625,13 @@ class Definitions {
626625
@tu lazy val ClassCastExceptionClass: ClassSymbol = requiredClass("java.lang.ClassCastException")
627626
@tu lazy val ClassCastExceptionClass_stringConstructor: TermSymbol = ClassCastExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
628627
case List(pt) =>
629-
pt.stripNull.isRef(StringClass)
628+
pt.stripNullWhenExplicit.isRef(StringClass)
630629
case _ => false
631630
}).symbol.asTerm
632631
@tu lazy val ArithmeticExceptionClass: ClassSymbol = requiredClass("java.lang.ArithmeticException")
633632
@tu lazy val ArithmeticExceptionClass_stringConstructor: TermSymbol = ArithmeticExceptionClass.info.member(nme.CONSTRUCTOR).suchThat(_.info.firstParamTypes match {
634633
case List(pt) =>
635-
pt.stripNull.isRef(StringClass)
634+
pt.stripNullWhenExplicit.isRef(StringClass)
636635
case _ => false
637636
}).symbol.asTerm
638637

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object NullOpsDecorator {
1717
* If this type isn't (syntactically) nullable, then returns the type unchanged.
1818
* The type will not be changed if explicit-nulls is not enabled.
1919
*/
20-
def stripNull(using Context): Type = {
20+
def stripNullWhenExplicit(using Context): Type = {
2121
def strip(tp: Type): Type =
2222
val tpWiden = tp.widenDealias
2323
val tpStriped = tpWiden match {
@@ -47,7 +47,7 @@ object NullOpsDecorator {
4747

4848
/** Is self (after widening and dealiasing) a type of the form `T | Null`? */
4949
def isNullableUnion(using Context): Boolean = {
50-
val stripped = self.stripNull
50+
val stripped = self.stripNullWhenExplicit
5151
stripped ne self
5252
}
5353

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ object Types {
833833
case OrNull(tp1) if Feature.unsafeNullsEnabled =>
834834
// Selecting `name` from a type `T | Null` is like selecting `name` from `T`, if
835835
// unsafeNulls is enabled. This can throw at runtime, but we trade soundness for usability.
836-
tp1.findMember(name, pre.stripNull, required, excluded)
836+
tp1.findMember(name, pre.stripNullWhenExplicit, required, excluded)
837837
case _ =>
838838
// we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix`
839839
// achieved that by narrowing `pre` to each alternative, but it led to merge errors in
@@ -3191,7 +3191,7 @@ object Types {
31913191
def apply(tp: Type)(using Context) =
31923192
if tp.isNullType then tp else OrType(tp, defn.NullType, soft = false)
31933193
def unapply(tp: Type)(using Context): Option[Type] =
3194-
val tp1 = tp.stripNull
3194+
val tp1 = tp.stripNullWhenExplicit
31953195
if tp1 ne tp then Some(tp1) else None
31963196
}
31973197

compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class ExpandSAMs extends MiniPhase:
5454
checkRefinements(tpe, fn)
5555
tree
5656
case tpe =>
57-
val tpe1 = checkRefinements(tpe.stripNull, fn)
57+
val tpe1 = checkRefinements(tpe.stripNullWhenExplicit, fn)
5858
val Seq(samDenot) = tpe1.possibleSamMethods
5959
cpy.Block(tree)(stats,
6060
AnonClass(tpe1 :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil))

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
211211
def filterStringConstructor(s: Symbol): Boolean = s.info match {
212212
case m: MethodType if s.isConstructor && m.paramInfos.size == 1 =>
213213
val head = m.paramInfos.head
214-
val pinfo = head.stripNull
214+
val pinfo = head.stripNullWhenExplicit
215215
pinfo == defn.StringType
216216
case _ => false
217217
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,7 @@ class Typer extends Namer
13171317
if (tree.tpt.isEmpty)
13181318
meth1.tpe.widen match {
13191319
case mt: MethodType =>
1320-
val pt1 = pt.stripNull
1320+
val pt1 = pt.stripNullWhenExplicit
13211321
pt1 match {
13221322
case SAMType(sam)
13231323
if !defn.isFunctionType(pt1)
@@ -1676,7 +1676,7 @@ class Typer extends Namer
16761676
}
16771677

16781678
def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(using Context): SeqLiteral = {
1679-
val elemProto = pt.stripNull.elemType match {
1679+
val elemProto = pt.stripNullWhenExplicit.elemType match {
16801680
case NoType => WildcardType
16811681
case bounds: TypeBounds => WildcardType(bounds)
16821682
case elemtp => elemtp

docs/docs/internals/explicit-nulls.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ title: "Explicit Nulls"
55

66
The explicit nulls feature (enabled via a flag) changes the Scala type hierarchy
77
so that reference types (e.g. `String`) are non-nullable. We can still express nullability
8-
with union types: e.g. `val x: String|Null = null`.
8+
with union types: e.g. `val x: String | Null = null`.
99

1010
The implementation of the feature in dotty can be conceptually divided in several parts:
1111
1. changes to the type hierarchy so that `Null` is only a subtype of `Any`
@@ -27,15 +27,13 @@ We change the type hierarchy so that `Null` is only a subtype of `Any` by:
2727

2828
## Working with Nullable Unions
2929

30-
There are some utility functions for nullable types in `NullOpsDecorator.scala` . They are extension methods for `Type`; hence we can use them in this way: `tp.f(...)`.
30+
There are some utility functions for nullable types in `NullOpsDecorator.scala`.
31+
They are extension methods for `Type`; hence we can use them in this way: `tp.f(...)`.
3132

32-
- `stripNull` syntactically strips all `Null` types in the union:
33+
- `stripNullWhenExplicit` syntactically strips all `Null` types in the union:
3334
e.g. `String|Null => String`.
34-
- `stripAllNullS` collapses all `Null` unions within this type, and not just the outermost
35-
ones (as `stripNull` does).
3635
- `isNullableUnion` determines whether `this` is a nullable union.
3736
- `isNullableAfterErasure` determines whether `this` type can have `null` value after erasure.
38-
- `isUnsafelyConvertible` determines whether we can convert `this` type to `pt` unsafely if we ignore `Null` type.
3937

4038
Within `Types.scala`, we also defined an extractor `OrNull` to extract the non-nullable part of a nullable unions .
4139

docs/docs/reference/other-new-features/explicit-nulls.md

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ So far, we have found the following useful:
5656

5757
Don't use `.nn` on mutable variables directly, because it may introduce an unknown type into the type of the variable.
5858

59+
- An `unsafeNulls` language feature
60+
61+
When imported, `T | Null` can be used as `T`, similar to regular Scala (without explicit nulls).
62+
63+
See UnsafeNulls section for more details.
64+
5965
## Unsoundness
6066

6167
The new type system is unsound with respect to `null`. This means there are still instances where an expression has a non-nullable type like `String`, but its value is actually `null`.
@@ -424,17 +430,38 @@ We don't support:
424430

425431
### UnsafeNulls
426432

427-
It is difficult to work with nullable values, we introduce a language feature `unsafeNulls`. Inside this "unsafe" scope, all `T | Null` values can be used as `T`, and `Null` type keeps being a subtype of `Any`.
433+
It is difficult to work with many nullable values, we introduce a language feature `unsafeNulls`.
434+
Inside this "unsafe" scope, all `T | Null` values can be used as `T`.
428435

429-
Users can import `scala.language.unsafeNulls` to create such scopes, or use `-language:unsafeNulls` to enable this feature globally. The following unsafe null operations will apply to all nullable types:
430-
1. select member of `T` on `T | Null` object
431-
2. call extension methods of `T` on `T | Null`
432-
3. convert `T1` to `T2` if `T1.stripAllNulls <:< T2.stripAllNulls` or `T1` is `Null` and `T2` has null value after erasure
433-
4. allow equality check between `T` and `T | Null`
436+
Users can import `scala.language.unsafeNulls` to create such scopes, or use `-language:unsafeNulls` to enable this feature globally (for migration purpose only).
437+
438+
Assume `T` is a reference type (a subtype of `AnyRef`), the following unsafe operation rules are
439+
applied in this unsafe-nulls scope:
440+
1. the members of `T` can be found on `T | Null`
441+
2. a value with type `T` can be compared with `T | Null` and `Null`
442+
3. suppose `T1` is not a subtype of `T2` using explicit-nulls subtyping (where `Null` is a direct
443+
subtype of Any), extension methods and implicit conversions designed for `T2` can be used for
444+
`T1` if `T1` is a subtype of `T2` using regular subtyping rules (where `Null` is a subtype of every
445+
reference type)
446+
4. suppose `T1` is not a subtype of `T2` using explicit-nulls subtyping, a value with type `T1`
447+
can be used as `T2` if `T1` is a subtype of `T2` using regular subtyping rules
434448

435449
Addtionally, `null` can be used as `AnyRef` (`Object`), which means you can select `.eq` or `.toString` on it.
436450

437-
The intention of this `unsafeNulls` is to give users a better migration path for explicit nulls. Projects for Scala 2 or regular dotty can try this by adding `-Yexplicit-nulls -language:unsafeNulls` to the compile options. A small number of manual modifications are expected (for example, some code relies on the fact of `Null <:< AnyRef`). To migrate to full explicit nulls in the future, `-language:unsafeNulls` can be dropped and add `import scala.language.unsafeNulls` only when needed.
451+
The program in `unsafeNulls` will have a **similar** semantic as regular Scala, but not **equivalent**.
452+
453+
For example, the following code cannot be compiled even using unsafe nulls. Because of the
454+
Java interoperation, the type of the get method becomes `T | Null`.
455+
456+
```Scala
457+
def head[T](xs: java.util.List[T]): T = xs.get(0) // error
458+
```
459+
460+
Since the compiler doesn’t know whether `T` is a reference type, it is unable to cast `T | Null`
461+
to `T`. A `.nn` need to be inserted after `xs.get(0)` by user manually to fix the error, which
462+
strips the `Nul`l from its type.
463+
464+
The intention of this `unsafeNulls` is to give users a better migration path for explicit nulls. Projects for Scala 2 or regular dotty can try this by adding `-Yexplicit-nulls -language:unsafeNulls` to the compile options. A small number of manual modifications are expected. To migrate to full explicit nulls in the future, `-language:unsafeNulls` can be dropped and add `import scala.language.unsafeNulls` only when needed.
438465

439466
```scala
440467
def f(x: String): String = ???

library/src/scala/runtime/stdLibPatches/language.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ object language:
101101
*/
102102
object `3.1`
103103

104-
/** Unsafe Nulls fot Explicit Nulls */
104+
/** Unsafe Nulls fot Explicit Nulls
105+
* Inside the "unsafe" scope, `Null` is considered as a subtype of all reference types.
106+
*
107+
* @see [[http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html]]
108+
*/
105109
object unsafeNulls
106110
end language

0 commit comments

Comments
 (0)