From e8bcc6e36a7101f09a57f719c8d05eab7b8a726f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 3 Dec 2020 11:11:11 +0100 Subject: [PATCH] Fix problem with comparing Null and value classes Consider: ```scala val x: Int = 0 val y: Int | Null = x // during erasure, x is boxed here, and Int | Null becomes Object val z: Int = y ``` The last line should be an error. But previously ``` C | Null <: C ``` succeeded for all classes `C` since it was deemed incorrectly that `Null` derivesFrom `C`. --- compiler/src/dotty/tools/dotc/core/Types.scala | 8 ++++++-- tests/neg/null-anyval.scala | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/neg/null-anyval.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 49faeff87ac4..720f0cc4ac55 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -264,9 +264,13 @@ object Types { // If the type is `T | Null` or `T | Nothing`, the class is != Nothing, // and `T` derivesFrom the class, then the OrType derivesFrom the class. // Otherwise, we need to check both sides derivesFrom the class. - if tp.tp1.isBottomType && cls != defn.NothingClass then + def isLowerBottomType(tp: Type) = + tp.isBottomType + && (tp.hasClassSymbol(defn.NothingClass) + || cls != defn.NothingClass && !cls.isValueClass) + if isLowerBottomType(tp.tp1) then loop(tp.tp2) - else if tp.tp2.isBottomType && cls != defn.NothingClass then + else if isLowerBottomType(tp.tp2) then loop(tp.tp1) else loop(tp.tp1) && loop(tp.tp2) diff --git a/tests/neg/null-anyval.scala b/tests/neg/null-anyval.scala new file mode 100644 index 000000000000..82b4b9ad9433 --- /dev/null +++ b/tests/neg/null-anyval.scala @@ -0,0 +1,12 @@ +object Test: + val x: Int = 0 + val y: Int | Null = x // during erasure, x is boxed here, and Int | Null becomes Object + val z0: Int = identity(y) // error + val z1: Int = identity[Int | Null](y) // error + val z2: Int = y // error + + class StrWrapper(x: String) extends AnyVal + val z3: StrWrapper = null // error + val z4: O.T = null // error +object O: + opaque type T = String