Skip to content

Commit 53c623e

Browse files
committed
Update doc
1 parent 200856c commit 53c623e

File tree

7 files changed

+29
-5
lines changed

7 files changed

+29
-5
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
939939
def appliedToTypeTrees(targs: List[Tree])(using Context): Tree =
940940
if targs.isEmpty then tree else
941941
val app = TypeApply(tree, targs)
942+
// If unsafe-nulls is enabled, a key is added to the Tree.
943+
// So a relaxed bound check can be used for these types In PostTyper.
942944
app.putAttachment(Nullables.UnsafeNullsKey, config.Feature.unsafeNullsEnabled)
943945
app
944946

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ object Mode {
123123
/** Should we try to convert values ignoring Null type? */
124124
val UnsafeNullConversion: Mode = newMode(28, "UnsafeNullConversion")
125125

126-
/** Unsafe Nulls SubType */
126+
/** Should we use Unsafe Nulls SubTyping in TypeComparer?
127+
* If this mode is in the Context, `Null` is considered as a
128+
* subtype of all reference type.
129+
*/
127130
val UnsafeNullsSubType: Mode = newMode(29, "UnsafeNullsSubType")
128131
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,8 @@ trait Applications extends Compatibility {
10821082
if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply()
10831083
else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
10841084
}
1085+
// If unsafe-nulls is enabled, a key is added to the Tree.
1086+
// So a relaxed bound check can be used for these types In PostTyper.
10851087
app.putAttachment(Nullables.UnsafeNullsKey, config.Feature.unsafeNullsEnabled)
10861088
app
10871089
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ object Nullables:
4949

5050
/** A StickyKey for TypeApply and AppliedType in unsafe nulls,
5151
* In PostTyper, a relaxed bound check is used for these types.
52+
* Since we are unable to track `unsafeNulls` after Typer, this
53+
* key is necessary.
5254
*/
5355
val UnsafeNullsKey = Property.StickyKey[Boolean]
5456

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,7 @@ class Typer extends Namer
778778
// A sequence argument `xs: _*` can be either a `Seq[T]` or an `Array[_ <: T]`,
779779
// irrespective of whether the method we're calling is a Java or Scala method,
780780
// so the expected type is the union `Seq[T] | Array[_ <: T]`.
781+
// If unsafe nulls is enabled, the expected type is `Seq[T | Null] | Array[_ <: T | Null] | Null`.
781782
val ptArg =
782783
// FIXME(#8680): Quoted patterns do not support Array repeated arguments
783784
if (ctx.mode.is(Mode.QuotedPattern))
@@ -796,8 +797,10 @@ class Typer extends Namer
796797
val expr0 = typedExpr(tree.expr, ptArg)
797798
val expr1 = if ctx.explicitNulls && (!ctx.mode.is(Mode.Pattern)) then
798799
if expr0.tpe.isNullType then
800+
// If the type of the argument is `Null`, we cast it to array directly.
799801
expr0.cast(pt.translateParameterized(defn.RepeatedParamClass, defn.ArrayClass))
800802
else
803+
// We need to make sure its type is no longer nullable
801804
expr0.castToNonNullable
802805
else expr0
803806
val fromCls = if expr1.tpe.derivesFrom(defn.ArrayClass, isErased = unsafeNullsEnabled)

docs/docs/internals/explicit-nulls.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ 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`
12-
2. a "translation layer" for Java interop that exposes the nullability in Java APIs
12+
2. a "translation layer" for Java interoperability that exposes the nullability in Java APIs
1313
3. a `unsafeNulls` language feature which enables implicit unsafe conversion between `T` and `T | Null`
1414

1515
## Explicit-Nulls Flag
@@ -21,7 +21,7 @@ The explicit-nulls flag is currently disabled by default. It can be enabled via
2121

2222
We change the type hierarchy so that `Null` is only a subtype of `Any` by:
2323
- modifying the notion of what is a nullable class (`isNullableClass`) in `SymDenotations`
24-
to include _only_ `Null` and `Any`
24+
to include _only_ `Null` and `Any`, which is used by `TypeComparer`
2525
- changing the parent of `Null` in `Definitions` to point to `Any` and not `AnyRef`
2626
- changing `isBottomType` and `isBottomClass` in `Definitions`
2727

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

3333
- `stripNullWhenExplicit` syntactically strips all `Null` types in the union:
34-
e.g. `String|Null => String`.
34+
e.g. `T | Null => T`. This should only be used if we can guarantee `T` is a reference type.
3535
- `isNullableUnion` determines whether `this` is a nullable union.
3636
- `isNullableAfterErasure` determines whether `this` type can have `null` value after erasure.
3737

@@ -43,7 +43,7 @@ Within `Types.scala`, we also defined an extractor `OrNull` to extract the non-n
4343
case _ => // otherwise
4444
```
4545

46-
## Java Interop
46+
## Java Interoperability
4747

4848
The problem we're trying to solve here is: if we see a Java method `String foo(String)`,
4949
what should that method look like to Scala?
@@ -79,6 +79,14 @@ The `matches` function in `Types.scala` is used to select condidated for overrid
7979

8080
The `compatibleTypes` in `RefCheck.scala` determines whether the overriding types are compatible.
8181

82+
## Nullified Upper Bound
83+
84+
Suppose we have a type bound `class C[T >: Null <: String]`, it becomes unapplicable in explicit nulls, since
85+
we don't have a type that is a supertype of `Null` and a subtype of `String`.
86+
87+
Hence, when we read a type bound from Scala 2 Tasty or Scala 3 Tasty, the upper bound is nullified if the lower
88+
bound is exactly `Null`. The example above would become `class C[T >: Null <: String | Null]`.
89+
8290
## Unsafe Nulls
8391

8492
The `unsafeNulls` language feature is currently disabled by default. It can be enabled by importing `scala.language.unsafeNulls` or using `-language:unsafeNulls`. The feature object is defined in `library/src/scalaShadowing/language.scala`. We can use `config.Feature.enabled(nme.unsafeNulls)` to check if this feature is enabled.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,12 +436,16 @@ Users can import `scala.language.unsafeNulls` to create such scopes, or use `-la
436436

437437
Assume `T` is a reference type (a subtype of `AnyRef`), the following unsafe operation rules are
438438
applied in this unsafe-nulls scope:
439+
439440
1. the members of `T` can be found on `T | Null`
441+
440442
2. a value with type `T` can be compared with `T | Null` and `Null`
443+
441444
3. suppose `T1` is not a subtype of `T2` using explicit-nulls subtyping (where `Null` is a direct
442445
subtype of Any), extension methods and implicit conversions designed for `T2` can be used for
443446
`T1` if `T1` is a subtype of `T2` using regular subtyping rules (where `Null` is a subtype of every
444447
reference type)
448+
445449
4. suppose `T1` is not a subtype of `T2` using explicit-nulls subtyping, a value with type `T1`
446450
can be used as `T2` if `T1` is a subtype of `T2` using regular subtyping rules
447451

0 commit comments

Comments
 (0)