Description
Compiler version
3.3.0-RC5
Problem at a glance
Null
is considered to be a subtype of anything that widenDealias
es to a nullable type.
Spec context
The spec actually says that Null <:< x.type
if the underlying type of x
is a nullable. But that is supposed to only apply to singleton types of the form path.type
. Currently in dotty, we even have absurd things like Null <:< "foo"
because "foo"
widens to "String"
which is nullable.
See https://scala-lang.org/files/archive/spec/2.13/03-types.html#singleton-types
Minimized code
REPL session without -Yexplicit-nulls
$ cs launch scala:3.3.0-RC5
Welcome to Scala 3.3.0-RC5 (1.8.0_362, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> val x: "foo" = null
val x: "foo" = null
scala> val y: 5 = null // Type error, as expected
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |val y: 5 = null // Type error, as expected
| ^^^^
|Found: Null
|Required: (5 : Int)
|Note that implicit conversions were not tried because the result of an implicit conversion
|must be more specific than (5 : Int)
|
| longer explanation available when compiling with `-explain`
1 error found
scala> val a: "foo" = "foo"
val a: "foo" = foo
scala> val b: a.type = null
val b: a.type = null
scala> val c: Any = new AnyRef
val c: Any = java.lang.Object@78d73b1b
scala> val d: c.type = null
val d: c.type = null
scala> val e: AnyVal = 5
val e: AnyVal = 5
scala> val f: e.type = null // Type error as expected
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |val f: e.type = null // Type error as expected
| ^^^^
|Found: Null
|Required: (e : AnyVal)
|Note that implicit conversions were not tried because the result of an implicit conversion
|must be more specific than (e : AnyVal)
|
| longer explanation available when compiling with `-explain`
1 error found
scala> summon[Null <:< "foo"]
val res0: Null =:= Null = generalized constraint
scala> summon[Null <:< c.type]
val res1: Null =:= Null = generalized constraint
scala> class Foo { def bar(): this.type = null }
// defined class Foo
Even under -Yexplicit-nulls
, we have issues:
$ cs launch scala:3.3.0-RC5 -- -Yexplicit-nulls
Welcome to Scala 3.3.0-RC5 (1.8.0_362, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> val x: "foo" = null // hey, progress!
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |val x: "foo" = null // hey, progress!
| ^^^^
|Found: Null
|Required: ("foo" : String)
|Note that implicit conversions were not tried because the result of an implicit conversion
|must be more specific than ("foo" : String)
|
| longer explanation available when compiling with `-explain`
1 error found
scala> val x: Any = "foo"
val x: Any = foo
scala> val y: x.type = null // oops, still broken
val y: x.type = null
scala> val a: String | Null = "foo"
val a: String | Null = foo
scala> val b: a.type = null // this is broken too
val b: a.type = null
scala> summon[Null <:< a.type]
val res0: Null =:= Null = generalized constraint
scala> type Foo = a.type
// defined alias type Foo = a.type
scala> summon[Null <:< Foo]
val res1: Null =:= Null = generalized constraint
Expectation
Not having Null <:< T
for arbitrary T
s that widenDealias
to a nullable type. Only allow it for singleton types (in dotc terminology, TermRef
s) whose direct underlying type is nullable, as the spec says.
Further, under -Yexplicit-nulls
, IMO we should take the opportunity to tighten the spec to remove the weird clause about the underlying type of singleton types.