-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Compiler version
3.3.0-RC5
Problem at a glance
Null is considered to be a subtype of anything that widenDealiases 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 FooEven 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 constraintExpectation
Not having Null <:< T for arbitrary Ts that widenDealias to a nullable type. Only allow it for singleton types (in dotc terminology, TermRefs) 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.