diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2016-04-15 16:41:03 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2016-04-20 10:13:02 +0200 |
commit | c600c15488b97e7b1f72e6b7811f53f03a493c1c (patch) | |
tree | 7bdba1077d32d370b166beca1de1c427d8821aff | |
parent | d6f66ec0f38e42c19f79cbe9d32d29c65dee1e05 (diff) | |
download | scala-c600c15488b97e7b1f72e6b7811f53f03a493c1c.tar.gz scala-c600c15488b97e7b1f72e6b7811f53f03a493c1c.tar.bz2 scala-c600c15488b97e7b1f72e6b7811f53f03a493c1c.zip |
SI-6710 / PR 5072 follow-up: fix Unit.box / Unit.unbox
The backend replaces .box / .unbox methods by corresponding invocations
to BoxesRunTime, but not for Unit.
This commit restores the body of `Unit.box` and `Unit.unbox`.
-rw-r--r-- | project/GenerateAnyVals.scala | 12 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 8 | ||||
-rw-r--r-- | src/library/scala/Unit.scala | 4 | ||||
-rw-r--r-- | test/junit/scala/BoxUnboxTest.scala | 119 |
4 files changed, 133 insertions, 10 deletions
diff --git a/project/GenerateAnyVals.scala b/project/GenerateAnyVals.scala index 921982aeec..84454cb0ed 100644 --- a/project/GenerateAnyVals.scala +++ b/project/GenerateAnyVals.scala @@ -225,7 +225,9 @@ import scala.language.implicitConversions""" "@unboxRunTimeDoc@" -> """ * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxTo%s`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. *""".format(name), - "@unboxDoc@" -> "the %s resulting from calling %sValue() on `x`".format(name, lcname) + "@unboxDoc@" -> "the %s resulting from calling %sValue() on `x`".format(name, lcname), + "@boxImpl@" -> "???", + "@unboxImpl@" -> "???" ) def interpolations = Map( "@name@" -> name, @@ -296,7 +298,7 @@ package scala * @param x the @name@ to be boxed * @return a @boxed@ offering `x` as its underlying value. */ -def box(x: @name@): @boxed@ = ??? +def box(x: @name@): @boxed@ = @boxImpl@ /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -306,7 +308,7 @@ def box(x: @name@): @boxed@ = ??? * @throws ClassCastException if the argument is not a @boxed@ * @return @unboxDoc@ */ -def unbox(x: java.lang.Object): @name@ = ??? +def unbox(x: java.lang.Object): @name@ = @unboxImpl@ /** The String representation of the scala.@name@ companion object. */ override def toString = "object scala.@name@" @@ -458,7 +460,9 @@ override def getClass(): Class[Boolean] = ??? override def boxUnboxInterpolations = Map( "@boxRunTimeDoc@" -> "", "@unboxRunTimeDoc@" -> "", - "@unboxDoc@" -> "the Unit value ()" + "@unboxDoc@" -> "the Unit value ()", + "@boxImpl@" -> "scala.runtime.BoxedUnit.UNIT", + "@unboxImpl@" -> "x.asInstanceOf[scala.runtime.BoxedUnit]" ) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 6d3d458324..cea264c57a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -658,16 +658,16 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface, attachment.sam) generatedType = methodBTypeFromSymbol(fun.symbol).returnType - case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) => + case Apply(fun, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) => val nativeKind = tpeTK(expr) genLoad(expr, nativeKind) val MethodNameAndType(mname, methodType) = srBoxesRuntimeBoxToMethods(nativeKind) bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos) - generatedType = boxResultType(fun.symbol) // was typeToBType(fun.symbol.tpe.resultType) + generatedType = boxResultType(fun.symbol) - case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) => + case Apply(fun, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) => genLoad(expr) - val boxType = unboxResultType(fun.symbol) // was typeToBType(fun.symbol.owner.linkedClassOfClass.tpe) + val boxType = unboxResultType(fun.symbol) generatedType = boxType val MethodNameAndType(mname, methodType) = srBoxesRuntimeUnboxToMethods(boxType) bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos) diff --git a/src/library/scala/Unit.scala b/src/library/scala/Unit.scala index 397d7b823b..eb6d1d0ddf 100644 --- a/src/library/scala/Unit.scala +++ b/src/library/scala/Unit.scala @@ -30,7 +30,7 @@ object Unit extends AnyValCompanion { * @param x the Unit to be boxed * @return a scala.runtime.BoxedUnit offering `x` as its underlying value. */ - def box(x: Unit): scala.runtime.BoxedUnit = ??? + def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -40,7 +40,7 @@ object Unit extends AnyValCompanion { * @throws ClassCastException if the argument is not a scala.runtime.BoxedUnit * @return the Unit value () */ - def unbox(x: java.lang.Object): Unit = ??? + def unbox(x: java.lang.Object): Unit = x.asInstanceOf[scala.runtime.BoxedUnit] /** The String representation of the scala.Unit companion object. */ override def toString = "object scala.Unit" diff --git a/test/junit/scala/BoxUnboxTest.scala b/test/junit/scala/BoxUnboxTest.scala new file mode 100644 index 0000000000..162d805a6b --- /dev/null +++ b/test/junit/scala/BoxUnboxTest.scala @@ -0,0 +1,119 @@ +package scala + +import org.junit.Test +import org.junit.Assert._ +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.AssertUtil._ + +@RunWith(classOf[JUnit4]) +class BoxUnboxTest { + def genericNull[T] = null.asInstanceOf[T] // allowed, see SI-4437, point 2 + + @Test + def boxUnboxInt(): Unit = { + val b = new Integer(1) + val u = 1 + + assertEquals(1.toInt, u) + + assertEquals(Predef.int2Integer(1), b) + assertEquals(1: Integer, b) + assertEquals(Int.box(1), b) + assertEquals(1.asInstanceOf[Object], b) + + assertThrows[ClassCastException]("".asInstanceOf[Integer]) + + assertEquals(Predef.Integer2int(b), u) + assertEquals(b: Int, u) + assertEquals(Int.unbox(b), u) + assertEquals(b.asInstanceOf[Int], u) + assertEquals(b.intValue, u) + assertEquals(b.toInt, u) + intWrapper(b).toInt + + assertThrows[ClassCastException](Int.unbox("")) + assertThrows[ClassCastException]("".asInstanceOf[Int]) + + // null unboxing in various positions + + val n1 = Int.unbox(null) + assertEquals(n1, 0) + val n2 = Predef.Integer2int(null) + assertEquals(n2, 0) + val n3 = (null: Integer): Int + assertEquals(n3, 0) + val n4 = null.asInstanceOf[Int] + assertEquals(n4, 0) + val n5 = null.asInstanceOf[Int] == 0 + assertTrue(n5) + val n6 = null.asInstanceOf[Int] == null // SI-9671 -- should be false, but is true + assertThrows[AssertionError](assertFalse(n6)) // should not throw + val n7 = null.asInstanceOf[Int] != 0 + assertFalse(n7) + val n8 = null.asInstanceOf[Int] != null // SI-9671 -- should be true, but is false + assertThrows[AssertionError](assertTrue(n8)) // should not throw + + val mp = new java.util.HashMap[Int, Int] + val n9 = mp.get(0) + assertEquals(n9, 0) + val n10 = mp.get(0) == null // SI-602 -- maybe related to SI-9671 (test above)? + assertThrows[AssertionError](assertFalse(n10)) // should not throw + + def f(a: Any) = "" + a + val n11 = f(null.asInstanceOf[Int]) // "null", should be "0". probably same cause as SI-602. + assertThrows[AssertionError](assertEquals(n11, "0")) // should not throw + + def n12 = genericNull[Int] + assertEquals(n12, 0) + } + + @Test + def numericConversions(): Unit = { + val i1 = 1L.asInstanceOf[Int] + assertEquals(i1, 1) + assertThrows[ClassCastException] { + val i2 = (1L: Any).asInstanceOf[Int] // SI-1448, should not throw. see also SI-4437 point 1. + assertEquals(i2, 1) + } + } + + @Test + def boxUnboxBoolean(): Unit = { + val n1 = Option(null.asInstanceOf[Boolean]) // SI-7397 -- should be Some(false), but is None + assertThrows[AssertionError](assertEquals(n1, Some(false))) // should not throw + } + + @Test + def boxUnboxUnit(): Unit = { + // should not use assertEquals in this test: it takes two Object parameters. normally, Unit does + // not conform to Object, but for Java-defined methods scalac makes an exception and treats them + // as Any. passing a Unit as Any makes the compiler go through another layer of boxing, so it + // can hide some bugs (where we actually have a null, but the compiler makes it a ()). + + var v = 0 + def eff() = { v = 1 } + def chk() = { assert(v == 1); v = 0 } + + val b = runtime.BoxedUnit.UNIT + + assert(eff() == b); chk() + assert(Unit.box(eff()) == b); chk() + assert(().asInstanceOf[Object] == b) + + Unit.unbox({eff(); b}); chk() + Unit.unbox({eff(); null}); chk() + assertThrows[ClassCastException](Unit.unbox({eff(); ""})); chk() + + val n1 = null.asInstanceOf[Unit] // SI-9066: should be UNIT, but currently null + assertThrows[AssertionError](assert(n1 == b)) // should not throw + + val n2 = null.asInstanceOf[Unit] == b // SI-9066: should be true, but currently false + assertThrows[AssertionError](assert(n2)) // should not throw + + def f(a: Any) = "" + a + val n3 = f(null.asInstanceOf[Unit]) // "null", should be "()". probably same cause as SI-602. + assertThrows[AssertionError](assertEquals(n3, "()")) // should not throw + } +} |