Skip to content

Commit a3a0b43

Browse files
tanishikingsjrd
andcommitted
Removes spurious js.AsInstanceOf around the js.LinkTimeIf
When B<:A and C<:A, `linkTimeIf[A](cond){ B }{ C }` would generate an `.asInstanceOf[A]` that casts the resulting value to a supertype. However, this cast is redundant, as the linker will prune one branch at linktime, and the remaining expression is already known to be a compatible type. This commit eliminates the surrounding `.asInstanceOf` around the `linkTimeIf[T]` when both `thenp` and `elsep` are surely subtypes of `T`. context: #5168 Co-authored-by: Sébastien Doeraene <sjrdoeraene@gmail.com>
1 parent b9b6a6d commit a3a0b43

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3207,7 +3207,21 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
32073207
case Object_isInstanceOf =>
32083208
genIsAsInstanceOf(obj, targs, cast = false)
32093209
case Object_asInstanceOf =>
3210-
genIsAsInstanceOf(obj, targs, cast = true)
3210+
val targetTpe = targs.head.tpe
3211+
obj match {
3212+
/* This is an optimization for `linkTimeIf(cond)(thenp)(elsep).asInstanceOf[T]`.
3213+
* If both `thenp` and `elsep` are subtypes of `T`, the `asInstanceOf`
3214+
* is redundant and can be removed.
3215+
*/
3216+
case Apply(fun, List(cond, thenp, elsep))
3217+
if fun.symbol == jsDefinitions.LinkingInfo_linkTimeIf &&
3218+
thenp.tpe <:< targetTpe && elsep.tpe <:< targetTpe =>
3219+
val genObj = genExpr(obj).asInstanceOf[js.LinkTimeIf]
3220+
// Create a new LinkTimeIf tree with the same children, with the type of the `asInstanceOf`.
3221+
js.LinkTimeIf(genObj.cond, genObj.thenp, genObj.elsep)(toIRType(targetTpe))(genObj.pos)
3222+
case _ =>
3223+
genIsAsInstanceOf(obj, targs, cast = true)
3224+
}
32113225
case Object_synchronized =>
32123226
genSynchronized(obj, args.head, isStat)
32133227
case _ =>

test-suite/js/src/test/scala/org/scalajs/testsuite/library/LinkTimeIfTest.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,48 @@ class LinkTimeIfTest {
9292
}
9393
assertEquals(pow(2.0, 8.0), 256.0, 0)
9494
}
95+
96+
// This test verifies that the compiler can safely compile surrounding
97+
// implicit asInstanceOf casts to a supertype.
98+
@Test def subtyping(): Unit = {
99+
trait A { def value: Int }
100+
class B(val value: Int) extends A
101+
class C(val value: Int) extends A
102+
103+
val b = new B(1)
104+
val c = new C(2)
105+
106+
val result1 = linkTimeIf[A](productionMode) { b } { c }
107+
assertEquals(if (Platform.isInProductionMode) b.value else c.value, result1.value)
108+
109+
val result2 = linkTimeIf[A](productionMode) { c } { b }
110+
assertEquals(if (Platform.isInProductionMode) c.value else b.value, result2.value)
111+
}
112+
113+
@Test def implPattern(): Unit = {
114+
sealed abstract class ArrayImpl {
115+
type Repr
116+
def empty(): Repr
117+
def length(v: Repr): Int
118+
}
119+
120+
object JSArrayImpl extends ArrayImpl {
121+
type Repr = js.Array[AnyRef]
122+
def empty(): Repr = js.Array[AnyRef]()
123+
def length(v: Repr): Int = v.length
124+
}
125+
126+
object ScalaArrayImpl extends ArrayImpl {
127+
type Repr = Array[AnyRef]
128+
def empty(): Repr = new Array[AnyRef](0)
129+
def length(v: Repr): Int = v.length
130+
}
131+
132+
val impl = linkTimeIf[ArrayImpl](productionMode) {
133+
JSArrayImpl
134+
} {
135+
ScalaArrayImpl
136+
}
137+
assertEquals(0, impl.length(impl.empty()))
138+
}
95139
}

0 commit comments

Comments
 (0)