Skip to content

Commit 1c16873

Browse files
committed
Stop dropping a cast, handle the missing cast in the bytecode assertion
1 parent b09fe7d commit 1c16873

File tree

3 files changed

+22
-11
lines changed

3 files changed

+22
-11
lines changed

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ class ArrayApply extends MiniPhase {
2020

2121
private val transformListApplyLimit = 8
2222

23-
override def transformTypeApply(tree: TypeApply)(using Context): Tree =
24-
stripCast(tree) match
25-
case app: Apply if isConsChain(app) => app
26-
case _ => tree
27-
2823
override def transformApply(tree: Apply)(using Context): Tree =
2924
if isArrayModuleApply(tree.symbol) then
3025
tree.args match
@@ -53,11 +48,6 @@ class ArrayApply extends MiniPhase {
5348

5449
else tree
5550

56-
private def isConsChain(tree: Tree)(using Context): Boolean = tree match
57-
case Apply(Select(New(tt), nme.CONSTRUCTOR), List(_, arg)) =>
58-
tt.symbol == defn.ConsClass && (arg.symbol == defn.NilModule || isConsChain(arg))
59-
case _ => false
60-
6151
private def isArrayModuleApply(sym: Symbol)(using Context): Boolean =
6252
sym.name == nme.apply
6353
&& (sym.owner == defn.ArrayModuleClass || (sym.owner == defn.IArrayModuleClass && !sym.is(Extension)))

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,24 @@ class ArrayApplyOptTest extends DottyBytecodeTest {
212212
val meth1 = getMethod(clsNode, "meth1")
213213
val meth2 = getMethod(clsNode, "meth2")
214214

215-
val instructions1 = instructionsFromMethod(meth1)
215+
val instructions1 = instructionsFromMethod(meth1) match
216+
case instr :+ TypeOp(CHECKCAST, _) :+ (ret @ Op(ARETURN)) =>
217+
// List.apply[?A] doesn't, strictly, return List[?A],
218+
// because it cascades to its definition on IterableFactory
219+
// where it returns CC[A]. The erasure of that is Object,
220+
// which is why Erasure's Typer adds a cast to compensate.
221+
// If we drop that cast while optimising (because using
222+
// the constructor for :: doesn't require the cast like
223+
// List.apply did) then then cons construction chain will
224+
// be typed as ::.
225+
// Unfortunately the LUB of :: and Nil.type is Product
226+
// instead of List, so a cast remains necessary,
227+
// across whatever causes the lub, like `if` or `try` branches.
228+
// Therefore if we dropping the cast may cause a needed cast
229+
// to be necessary, we shouldn't drop the cast,
230+
// which was only motivated by the assert here.
231+
instr :+ ret
232+
case instr => instr
216233
val instructions2 = instructionsFromMethod(meth2)
217234

218235
assert(instructions1 == instructions2,

tests/run/list-apply-eval.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ object Test:
2222

2323
// just assert it doesn't throw CCE to List
2424
val queue = scala.collection.mutable.Queue[String]()
25+
26+
// test for the cast instruction described in checkApplyAvoidsIntermediateArray
27+
def lub(b: Boolean): List[(String, String)] =
28+
if b then List(("foo", "bar")) else Nil

0 commit comments

Comments
 (0)