Skip to content

Commit 2ec7e79

Browse files
committed
Merge pull request scala#5100 from lrytz/unitBox
SI-6710 / PR 5072 follow-up: fix Unit.box / Unit.unbox
2 parents 4f2a20e + c600c15 commit 2ec7e79

File tree

4 files changed

+133
-10
lines changed

4 files changed

+133
-10
lines changed

project/GenerateAnyVals.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ import scala.language.implicitConversions"""
225225
"@unboxRunTimeDoc@" -> """
226226
* Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxTo%s`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]].
227227
*""".format(name),
228-
"@unboxDoc@" -> "the %s resulting from calling %sValue() on `x`".format(name, lcname)
228+
"@unboxDoc@" -> "the %s resulting from calling %sValue() on `x`".format(name, lcname),
229+
"@boxImpl@" -> "???",
230+
"@unboxImpl@" -> "???"
229231
)
230232
def interpolations = Map(
231233
"@name@" -> name,
@@ -296,7 +298,7 @@ package scala
296298
* @param x the @name@ to be boxed
297299
* @return a @boxed@ offering `x` as its underlying value.
298300
*/
299-
def box(x: @name@): @boxed@ = ???
301+
def box(x: @name@): @boxed@ = @boxImpl@
300302
301303
/** Transform a boxed type into a value type. Note that this
302304
* method is not typesafe: it accepts any Object, but will throw
@@ -306,7 +308,7 @@ def box(x: @name@): @boxed@ = ???
306308
* @throws ClassCastException if the argument is not a @boxed@
307309
* @return @unboxDoc@
308310
*/
309-
def unbox(x: java.lang.Object): @name@ = ???
311+
def unbox(x: java.lang.Object): @name@ = @unboxImpl@
310312
311313
/** The String representation of the scala.@name@ companion object. */
312314
override def toString = "object scala.@name@"
@@ -458,7 +460,9 @@ override def getClass(): Class[Boolean] = ???
458460
override def boxUnboxInterpolations = Map(
459461
"@boxRunTimeDoc@" -> "",
460462
"@unboxRunTimeDoc@" -> "",
461-
"@unboxDoc@" -> "the Unit value ()"
463+
"@unboxDoc@" -> "the Unit value ()",
464+
"@boxImpl@" -> "scala.runtime.BoxedUnit.UNIT",
465+
"@unboxImpl@" -> "x.asInstanceOf[scala.runtime.BoxedUnit]"
462466
)
463467
}
464468

src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,16 +658,16 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
658658
genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface, attachment.sam)
659659
generatedType = methodBTypeFromSymbol(fun.symbol).returnType
660660

661-
case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) =>
661+
case Apply(fun, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) =>
662662
val nativeKind = tpeTK(expr)
663663
genLoad(expr, nativeKind)
664664
val MethodNameAndType(mname, methodType) = srBoxesRuntimeBoxToMethods(nativeKind)
665665
bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)
666-
generatedType = boxResultType(fun.symbol) // was typeToBType(fun.symbol.tpe.resultType)
666+
generatedType = boxResultType(fun.symbol)
667667

668-
case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
668+
case Apply(fun, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
669669
genLoad(expr)
670-
val boxType = unboxResultType(fun.symbol) // was typeToBType(fun.symbol.owner.linkedClassOfClass.tpe)
670+
val boxType = unboxResultType(fun.symbol)
671671
generatedType = boxType
672672
val MethodNameAndType(mname, methodType) = srBoxesRuntimeUnboxToMethods(boxType)
673673
bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)

src/library/scala/Unit.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ object Unit extends AnyValCompanion {
3030
* @param x the Unit to be boxed
3131
* @return a scala.runtime.BoxedUnit offering `x` as its underlying value.
3232
*/
33-
def box(x: Unit): scala.runtime.BoxedUnit = ???
33+
def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT
3434

3535
/** Transform a boxed type into a value type. Note that this
3636
* method is not typesafe: it accepts any Object, but will throw
@@ -40,7 +40,7 @@ object Unit extends AnyValCompanion {
4040
* @throws ClassCastException if the argument is not a scala.runtime.BoxedUnit
4141
* @return the Unit value ()
4242
*/
43-
def unbox(x: java.lang.Object): Unit = ???
43+
def unbox(x: java.lang.Object): Unit = x.asInstanceOf[scala.runtime.BoxedUnit]
4444

4545
/** The String representation of the scala.Unit companion object. */
4646
override def toString = "object scala.Unit"
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package scala
2+
3+
import org.junit.Test
4+
import org.junit.Assert._
5+
import org.junit.runner.RunWith
6+
import org.junit.runners.JUnit4
7+
8+
import scala.tools.testing.AssertUtil._
9+
10+
@RunWith(classOf[JUnit4])
11+
class BoxUnboxTest {
12+
def genericNull[T] = null.asInstanceOf[T] // allowed, see SI-4437, point 2
13+
14+
@Test
15+
def boxUnboxInt(): Unit = {
16+
val b = new Integer(1)
17+
val u = 1
18+
19+
assertEquals(1.toInt, u)
20+
21+
assertEquals(Predef.int2Integer(1), b)
22+
assertEquals(1: Integer, b)
23+
assertEquals(Int.box(1), b)
24+
assertEquals(1.asInstanceOf[Object], b)
25+
26+
assertThrows[ClassCastException]("".asInstanceOf[Integer])
27+
28+
assertEquals(Predef.Integer2int(b), u)
29+
assertEquals(b: Int, u)
30+
assertEquals(Int.unbox(b), u)
31+
assertEquals(b.asInstanceOf[Int], u)
32+
assertEquals(b.intValue, u)
33+
assertEquals(b.toInt, u)
34+
intWrapper(b).toInt
35+
36+
assertThrows[ClassCastException](Int.unbox(""))
37+
assertThrows[ClassCastException]("".asInstanceOf[Int])
38+
39+
// null unboxing in various positions
40+
41+
val n1 = Int.unbox(null)
42+
assertEquals(n1, 0)
43+
val n2 = Predef.Integer2int(null)
44+
assertEquals(n2, 0)
45+
val n3 = (null: Integer): Int
46+
assertEquals(n3, 0)
47+
val n4 = null.asInstanceOf[Int]
48+
assertEquals(n4, 0)
49+
val n5 = null.asInstanceOf[Int] == 0
50+
assertTrue(n5)
51+
val n6 = null.asInstanceOf[Int] == null // SI-9671 -- should be false, but is true
52+
assertThrows[AssertionError](assertFalse(n6)) // should not throw
53+
val n7 = null.asInstanceOf[Int] != 0
54+
assertFalse(n7)
55+
val n8 = null.asInstanceOf[Int] != null // SI-9671 -- should be true, but is false
56+
assertThrows[AssertionError](assertTrue(n8)) // should not throw
57+
58+
val mp = new java.util.HashMap[Int, Int]
59+
val n9 = mp.get(0)
60+
assertEquals(n9, 0)
61+
val n10 = mp.get(0) == null // SI-602 -- maybe related to SI-9671 (test above)?
62+
assertThrows[AssertionError](assertFalse(n10)) // should not throw
63+
64+
def f(a: Any) = "" + a
65+
val n11 = f(null.asInstanceOf[Int]) // "null", should be "0". probably same cause as SI-602.
66+
assertThrows[AssertionError](assertEquals(n11, "0")) // should not throw
67+
68+
def n12 = genericNull[Int]
69+
assertEquals(n12, 0)
70+
}
71+
72+
@Test
73+
def numericConversions(): Unit = {
74+
val i1 = 1L.asInstanceOf[Int]
75+
assertEquals(i1, 1)
76+
assertThrows[ClassCastException] {
77+
val i2 = (1L: Any).asInstanceOf[Int] // SI-1448, should not throw. see also SI-4437 point 1.
78+
assertEquals(i2, 1)
79+
}
80+
}
81+
82+
@Test
83+
def boxUnboxBoolean(): Unit = {
84+
val n1 = Option(null.asInstanceOf[Boolean]) // SI-7397 -- should be Some(false), but is None
85+
assertThrows[AssertionError](assertEquals(n1, Some(false))) // should not throw
86+
}
87+
88+
@Test
89+
def boxUnboxUnit(): Unit = {
90+
// should not use assertEquals in this test: it takes two Object parameters. normally, Unit does
91+
// not conform to Object, but for Java-defined methods scalac makes an exception and treats them
92+
// as Any. passing a Unit as Any makes the compiler go through another layer of boxing, so it
93+
// can hide some bugs (where we actually have a null, but the compiler makes it a ()).
94+
95+
var v = 0
96+
def eff() = { v = 1 }
97+
def chk() = { assert(v == 1); v = 0 }
98+
99+
val b = runtime.BoxedUnit.UNIT
100+
101+
assert(eff() == b); chk()
102+
assert(Unit.box(eff()) == b); chk()
103+
assert(().asInstanceOf[Object] == b)
104+
105+
Unit.unbox({eff(); b}); chk()
106+
Unit.unbox({eff(); null}); chk()
107+
assertThrows[ClassCastException](Unit.unbox({eff(); ""})); chk()
108+
109+
val n1 = null.asInstanceOf[Unit] // SI-9066: should be UNIT, but currently null
110+
assertThrows[AssertionError](assert(n1 == b)) // should not throw
111+
112+
val n2 = null.asInstanceOf[Unit] == b // SI-9066: should be true, but currently false
113+
assertThrows[AssertionError](assert(n2)) // should not throw
114+
115+
def f(a: Any) = "" + a
116+
val n3 = f(null.asInstanceOf[Unit]) // "null", should be "()". probably same cause as SI-602.
117+
assertThrows[AssertionError](assertEquals(n3, "()")) // should not throw
118+
}
119+
}

0 commit comments

Comments
 (0)