Skip to content

Commit ca7c6fc

Browse files
authored
Merge pull request #11699 from ShapelessCat/fix-docs_explicit-nulls
Fix and improve the document for Explicit Nulls
2 parents 4af1386 + 99ee473 commit ca7c6fc

File tree

2 files changed

+219
-198
lines changed

2 files changed

+219
-198
lines changed

docs/docs/internals/explicit-nulls.md

+20-15
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ 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
88
with union types: e.g. `val x: String | Null = null`.
99

10-
The implementation of the feature in dotty can be conceptually divided in several parts:
11-
1. changes to the type hierarchy so that `Null` is only a subtype of `Any`
12-
2. a "translation layer" for Java interoperability that exposes the nullability in Java APIs
13-
3. a `unsafeNulls` language feature which enables implicit unsafe conversion between `T` and `T | Null`
10+
The implementation of the feature in Scala 3 can be conceptually divided in several parts:
11+
12+
1. changes to the type hierarchy so that `Null` is only a subtype of `Any`
13+
2. a "translation layer" for Java interoperability that exposes the nullability in Java APIs
14+
3. a `unsafeNulls` language feature which enables implicit unsafe conversion between `T` and `T | Null`
1415

1516
## Explicit-Nulls Flag
1617

@@ -20,10 +21,11 @@ The explicit-nulls flag is currently disabled by default. It can be enabled via
2021
## Type Hierarchy
2122

2223
We change the type hierarchy so that `Null` is only a subtype of `Any` by:
23-
- modifying the notion of what is a nullable class (`isNullableClass`) in `SymDenotations`
24-
to include _only_ `Null` and `Any`, which is used by `TypeComparer`
25-
- changing the parent of `Null` in `Definitions` to point to `Any` and not `AnyRef`
26-
- changing `isBottomType` and `isBottomClass` in `Definitions`
24+
25+
- modifying the notion of what is a nullable class (`isNullableClass`) in `SymDenotations`
26+
to include _only_ `Null` and `Any`, which is used by `TypeComparer`
27+
- changing the parent of `Null` in `Definitions` to point to `Any` and not `AnyRef`
28+
- changing `isBottomType` and `isBottomClass` in `Definitions`
2729

2830
## Working with Nullable Unions
2931

@@ -47,13 +49,15 @@ Within `Types.scala`, we also defined an extractor `OrNull` to extract the non-n
4749

4850
The problem we're trying to solve here is: if we see a Java method `String foo(String)`,
4951
what should that method look like to Scala?
50-
- since we should be able to pass `null` into Java methods, the argument type should be `String | Null`
51-
- since Java methods might return `null`, the return type should be `String | Null`
52+
53+
- since we should be able to pass `null` into Java methods, the argument type should be `String | Null`
54+
- since Java methods might return `null`, the return type should be `String | Null`
5255

5356
At a high-level:
54-
- we track the loading of Java fields and methods as they're loaded by the compiler
55-
- we do this in two places: `Namer` (for Java sources) and `ClassFileParser` (for bytecode)
56-
- whenever we load a Java member, we "nullify" its argument and return types
57+
58+
- we track the loading of Java fields and methods as they're loaded by the compiler
59+
- we do this in two places: `Namer` (for Java sources) and `ClassFileParser` (for bytecode)
60+
- whenever we load a Java member, we "nullify" its argument and return types
5761

5862
The nullification logic lives in `compiler/src/dotty/tools/dotc/core/JavaNullInterop.scala`.
5963

@@ -117,7 +121,7 @@ abstract class Node:
117121
val next: Node | Null
118122

119123
def f =
120-
val l: Node|Null = ???
124+
val l: Node | Null = ???
121125
if l != null && l.next != null then
122126
val third: l.next.next.type = l.next.next
123127
```
@@ -126,11 +130,12 @@ After typing, `f` becomes:
126130

127131
```scala
128132
def f =
129-
val l: Node|Null = ???
133+
val l: Node | Null = ???
130134
if l != null && l.$asInstanceOf$[l.type & Node].next != null then
131135
val third:
132136
l.$asInstanceOf$[l.type & Node].next.$asInstanceOf$[(l.type & Node).next.type & Node].next.type =
133137
l.$asInstanceOf$[l.type & Node].next.$asInstanceOf$[(l.type & Node).next.type & Node].next
134138
```
139+
135140
Notice that in the example above `(l.type & Node).next.type & Node` is still a stable path, so
136141
we can use it in the type and track it for flow typing.

0 commit comments

Comments
 (0)