Skip to content

Check if community build used |Nothing signature #14998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,19 @@ object TypeErasure {
* which leads to more predictable bytecode and (?) faster dynamic dispatch.
*/
def erasedLub(tp1: Type, tp2: Type)(using Context): Type = {
// After erasure, C | {Null, Nothing} is just C, if C is a reference type.
// We need to short-circuit this case here because the regular lub logic below
// relies on the class hierarchy, which doesn't properly capture `Null`s subtyping
// behaviour.
if (tp1.isBottomTypeAfterErasure && tp2.derivesFrom(defn.ObjectClass)) return tp2
if (tp2.isBottomTypeAfterErasure && tp1.derivesFrom(defn.ObjectClass)) return tp1
tp1 match {
// We need to short-circuit the following 2 case because the regular lub logic in the else relies on
// the class hierarchy, which doesn't properly capture `Nothing`/`Null` subtyping behaviour.
if (tp1.isRef(defn.NothingClass) || tp1.isRef(defn.NullClass)) && tp2.derivesFrom(defn.ObjectClass) then
tp2 // After erasure, {Nothing|Null} | C is just C, if C is a reference type.
else if (tp2.isRef(defn.NothingClass) || tp2.isRef(defn.NullClass)) && tp1.derivesFrom(defn.ObjectClass) then
tp1 // After erasure, C | {Nothing|Null} is just C, if C is a reference type.
else if tp1.isRef(defn.NothingClass) then
report.error(em"erasedLub($tp1, $tp2)")
tp2 // After erasure, Nothing | T is just T
else if tp2.isRef(defn.NothingClass) then
report.error(em"erasedLub($tp1, $tp2)")
tp1 // After erasure, T | Nothing is just T
else tp1 match {
case JavaArrayType(elem1) =>
import dotty.tools.dotc.transform.TypeUtils._
tp2 match {
Expand Down
23 changes: 22 additions & 1 deletion compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import scala.tools.asm.Opcodes
import scala.jdk.CollectionConverters._
import Opcodes._

class TestBCode extends DottyBytecodeTest {
class DottyBytecodeTests extends DottyBytecodeTest {
import ASMConverters._
@Test def nullChecks = {
val source = """
Expand Down Expand Up @@ -1234,6 +1234,27 @@ class TestBCode extends DottyBytecodeTest {
Label(9), Op(IRETURN)))
}
}

/** Check that erasure if `Int | Nothing` is `int` */
// @Test def i14970 = {
// val source =
// s"""class Foo {
// | def foo: Int | Nothing = 1
// | def bar: Nothing | Int = 1
// |}
// """.stripMargin

// checkBCode(source) { dir =>
// val clsIn = dir.lookupName("Foo.class", directory = false).input
// val clsNode = loadClassNode(clsIn)
// def testSig(methodName: String, expectedSignature: String) = {
// val signature = clsNode.methods.asScala.filter(_.name == methodName).map(_.signature)
// assertEquals(List(expectedSignature), signature)
// }
// testSig("foo", "()I")
// testSig("bar", "()I")
// }
// }
}

object invocationReceiversTestCode {
Expand Down
7 changes: 7 additions & 0 deletions tests/neg/i14964.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// nopos-error
// nopos-error
@main def Test: Unit =
val xs = (1, 2).toList
val a1 = xs.toArray
println((1, 2).toList.toArray)
val a2 = (1, 2).toList.toArray
6 changes: 6 additions & 0 deletions tests/neg/i14964b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// nopos-error
@main def Test: Unit =
println(summon[reflect.ClassTag[Int]]) // classOf[Int]
println(summon[reflect.ClassTag[Int | Int]]) // classOf[Int]
println(summon[reflect.ClassTag[Int | 1]]) // classOf[Int]
println(summon[reflect.ClassTag[Int | Nothing]]) // classOf[Object]
12 changes: 9 additions & 3 deletions tests/neg/i5823.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// nopos-error
// nopos-error
// nopos-error
// nopos-error
// Test that `C|Null` is erased to `C` if `C` is
// a reference type.
// If `C` is a value type, then `C|Null = Object`.
Expand All @@ -7,7 +11,7 @@ class A
class B

class Foo {

// ok, because A and B are <: Object.
def foo(a: A|Null): Unit = ()
def foo(b: B|Null): Unit = ()
Expand All @@ -26,13 +30,15 @@ class Foo {
def foo2(a: A|Nothing): Unit = ()
def foo2(b: B|Nothing): Unit = ()

// ok because erased to primitive types
def bar2(a: Int|Nothing): Unit = ()
def bar2(b: Boolean|Nothing): Unit = () // error: signatures match
def bar2(b: Boolean|Nothing): Unit = ()

// ok, T is erased to `String` and `Integer`, respectively
def gen3[T <: String](s: T|Nothing): Unit = ()
def gen3[T <: Integer](i: T|Nothing): Unit = ()

// ok because erased to primitive types
def gen4[T <: Int](i: T|Nothing): Unit = ()
def gen4[T <: Boolean](b: T|Nothing): Unit = () // error: signatures match
def gen4[T <: Boolean](b: T|Nothing): Unit = ()
}
4 changes: 4 additions & 0 deletions tests/run/i14964b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Int
Int
Int
Int