From 1fb2206ec40506022b48eac2eb94a8d7d95d1197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 11 May 2023 16:27:00 +0200 Subject: [PATCH] Under explicit nulls, remove the rule `Null <:< x.type`. The specified rule that `Null <:< x.type` when the underlying type `U` of `x` is nullable is dubious to begin with. Under explicit nulls, it becomes decidedly out of place. We now disable that rule under `-Yexplicit-nulls`. --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- tests/explicit-nulls/neg/i17467.check | 31 +++++++++++++++++++ tests/explicit-nulls/neg/i17467.scala | 16 ++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/explicit-nulls/neg/i17467.check create mode 100644 tests/explicit-nulls/neg/i17467.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 3b2cb0db78c3..159102e1f875 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -912,7 +912,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling // scala.AnyRef [Scala 3: which scala.Null conforms to], the type denotes the set of values consisting // of null and the value denoted by p (i.e., the value v for which v eq p). [Otherwise,] the type // denotes the set consisting of only the value denoted by p. - isNullable(tp.underlying) && tp.isStable + !ctx.explicitNulls && isNullable(tp.underlying) && tp.isStable case tp: RefinedOrRecType => isNullable(tp.parent) case tp: AppliedType => isNullable(tp.tycon) case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2) diff --git a/tests/explicit-nulls/neg/i17467.check b/tests/explicit-nulls/neg/i17467.check new file mode 100644 index 000000000000..3b5e556ba36d --- /dev/null +++ b/tests/explicit-nulls/neg/i17467.check @@ -0,0 +1,31 @@ +-- [E007] Type Mismatch Error: tests/explicit-nulls/neg/i17467.scala:4:22 ---------------------------------------------- +4 | val a2: a1.type = null // error + | ^^^^ + | Found: Null + | Required: (a1 : String) + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than (a1 : String) + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/explicit-nulls/neg/i17467.scala:7:22 ---------------------------------------------- +7 | val b2: b1.type = null // error + | ^^^^ + | Found: Null + | Required: (b1 : String | Null) + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than (b1 : String | Null) + | + | longer explanation available when compiling with `-explain` +-- [E172] Type Error: tests/explicit-nulls/neg/i17467.scala:8:28 ------------------------------------------------------- +8 | summon[Null <:< b1.type] // error + | ^ + | Cannot prove that Null <:< (b1 : String | Null). +-- [E007] Type Mismatch Error: tests/explicit-nulls/neg/i17467.scala:14:22 --------------------------------------------- +14 | val c2: c1.type = null // error + | ^^^^ + | Found: Null + | Required: (c1 : Null) + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than (c1 : Null) + | + | longer explanation available when compiling with `-explain` diff --git a/tests/explicit-nulls/neg/i17467.scala b/tests/explicit-nulls/neg/i17467.scala new file mode 100644 index 000000000000..2e96252f6759 --- /dev/null +++ b/tests/explicit-nulls/neg/i17467.scala @@ -0,0 +1,16 @@ +object Test: + def test(): Unit = + val a1: String = "foo" + val a2: a1.type = null // error + + val b1: String | Null = "foo" + val b2: b1.type = null // error + summon[Null <:< b1.type] // error + + /* The following would be sound, but it would require a specific subtyping + * rule (and implementation code) for debatable value. So it is an error. + */ + val c1: Null = null + val c2: c1.type = null // error + end test +end Test