Skip to content

Commit 3d33bae

Browse files
committed
Fix #505: Remove unnecessary boxing of synchronized block
`synchronized` is now treated as a magic method by the compiler and is no longer erased (similarly to `asInstanceOf` and `isInstanceOf`). Backend is updated to handle this new magic polymorphic method.
1 parent f47c720 commit 3d33bae

File tree

6 files changed

+47
-8
lines changed

6 files changed

+47
-8
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
116116
val Throwable_Type: Type = defn.ThrowableType
117117
val Object_isInstanceOf: Symbol = defn.Any_isInstanceOf
118118
val Object_asInstanceOf: Symbol = defn.Any_asInstanceOf
119+
val Object_synchronized: Symbol = defn.Object_synchronized
119120
val Object_equals: Symbol = defn.Any_equals
120121
val ArrayClass: Symbol = defn.ArrayClass
121122
val UnitClass: Symbol = defn.UnitClass

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,7 @@ class Definitions {
10741074
lazy val NoInitClasses: Set[Symbol] = NotRuntimeClasses + FunctionXXLClass
10751075

10761076
def isPolymorphicAfterErasure(sym: Symbol): Boolean =
1077-
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
1077+
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) || (sym eq Object_synchronized)
10781078

10791079
def isTupleType(tp: Type)(implicit ctx: Context): Boolean = {
10801080
val arity = tp.dealias.argInfos.length

compiler/src/dotty/tools/dotc/transform/LazyVals.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
167167
val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos)
168168
val rhs = x.rhs.changeOwnerAfter(x.symbol, initSymbol, this)
169169
val initialize = holderRef.select(lazyNme.initialize).appliedTo(rhs)
170-
val initBody =
171-
adaptToType(
172-
holderRef.select(defn.Object_synchronized).appliedTo(
173-
adaptToType(If(initialized, getValue, initialize), defn.ObjectType)),
174-
tpe)
170+
val initBody = holderRef
171+
.select(defn.Object_synchronized)
172+
.appliedToType(tpe)
173+
.appliedTo(If(initialized, getValue, initialize).ensureConforms(tpe))
175174
val initTree = DefDef(initSymbol, initBody)
176175

177176
// def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute()

compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ class TestBCode extends DottyBytecodeTest {
504504
/** Test that the size of the lazy val initialiazer is under a certain threshold
505505
*
506506
* - Fix to #5340 reduced the size from 39 instructions to 34
507+
* - Fix to #505 reduced the size from 34 instructions to 32
507508
*/
508509
@Test def i5340 = {
509510
val source =
@@ -519,7 +520,30 @@ class TestBCode extends DottyBytecodeTest {
519520
val clsIn = dir.lookupName("Test.class", directory = false).input
520521
val clsNode = loadClassNode(clsIn)
521522
val method = getMethod(clsNode, "x$lzyINIT1$1")
522-
assertEquals(34, instructionsFromMethod(method).size)
523+
assertEquals(32, instructionsFromMethod(method).size)
524+
}
525+
}
526+
527+
/** Test that synchronize blocks don't box */
528+
@Test def i505 = {
529+
val source =
530+
"""class Test {
531+
| def test: Int = synchronized(1)
532+
|}
533+
""".stripMargin
534+
535+
checkBCode(source) { dir =>
536+
val clsIn = dir.lookupName("Test.class", directory = false).input
537+
val clsNode = loadClassNode(clsIn)
538+
val method = getMethod(clsNode, "test")
539+
540+
val doBox = instructionsFromMethod(method).exists {
541+
case Invoke(_, _, name, _, _) =>
542+
name == "boxToInteger" || name == "unboxToInt"
543+
case _ =>
544+
false
545+
}
546+
assertFalse(doBox)
523547
}
524548
}
525549
}

tests/run/i505.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
val a: Int = synchronized(1)
4+
val b: Long = synchronized(1L)
5+
val c: Boolean = synchronized(true)
6+
val d: Float = synchronized(1f)
7+
val e: Double = synchronized(1.0)
8+
val f: Byte = synchronized(1.toByte)
9+
val g: Char = synchronized('1')
10+
val h: Short = synchronized(1.toShort)
11+
val i: String = synchronized("Hello")
12+
val j: List[Int] = synchronized(List(1))
13+
synchronized(())
14+
}
15+
}

0 commit comments

Comments
 (0)