Skip to content

ClassCastException when calling a trait method with call-by-name argument if implemented as single abstract method #11237

Open
scala/scala
#10830
@ruslan2009

Description

@ruslan2009

The following code:

trait Semigroup[F] { self =>
  def append(f1: F, f2: => F): F
  val z = 10
}

object bug extends App {
  case class Box(i: Int)
  val boxSemigroup: Semigroup[Box] = (x1, x2) => Box(Math.max(x1.i, x2.i))
  println(boxSemigroup.append(Box(1), Box(2)))
}

crashes at runtime with ClassCastException

Exception in thread "main" java.lang.ClassCastException: bug$Box cannot be cast to scala.Function0
	at bug$$anonfun$1.append(bug.scala:75)
	at bug$$anonfun$1.append(bug.scala:75)
	at bug$.delayedEndpoint$bug$1(bug.scala:76)
	at bug$delayedInit$body.apply(bug.scala:73)
	at scala.Function0.apply$mcV$sp(Function0.scala:34)
	at scala.Function0.apply$mcV$sp$(Function0.scala:34)
	at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
	at scala.App.$anonfun$main$1$adapted(App.scala:76)
	at scala.collection.immutable.List.foreach(List.scala:389)
	at scala.App.main(App.scala:76)
	at scala.App.main$(App.scala:74)
	at bug$.main(bug.scala:73)
	at bug.main(bug.scala)

Seems like the bytecode generated contains erroneous call to function representing call-by-name argument (instruction at 2) and then casting of it's result to Function0 (instruction at 7):

  public final bug$Box append(bug$Box, scala.Function0<bug$Box>);
    Code:
       0: aload_1
       1: aload_2
       2: invokeinterface #35,  1           // InterfaceMethod scala/Function0.apply:()Ljava/lang/Object;
       7: checkcast     #31                 // class scala/Function0
      10: invokestatic  #38                 // Method bug$.bug$$$anonfun$boxSemigroup$1:(Lbug$Box;Lscala/Function0;)Lbug$Box;
      13: areturn

Also note that if name of call-by-name argument in trait and SAM implementation are the same as in:

trait Semigroup[F] { self =>
  def append(f1: F, x2: => F): F
  val z = 10
}

object bug extends App {
  case class Box(i: Int)
  val boxSemigroup: Semigroup[Box] = (x1, x2) => Box(Math.max(x1.i, x2.i))
  println(boxSemigroup.append(Box(1), Box(2)))
}

then it works as expected and compiler generates correct bytecode for this function:

  public final bug$Box append(bug$Box, scala.Function0<bug$Box>);
    Code:
       0: aload_1
       1: aload_2
       2: invokestatic  #32                 // Method bug$.bug$$$anonfun$boxSemigroup$1:(Lbug$Box;Lscala/Function0;)Lbug$Box;
       5: areturn

Tried with Scala versions 2.12.7 and 2.13.0-M5.

Also seems like this issue is related to #10362 since same exception is being thrown at runtime and similar bytecode is being generated in both cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bynamefixed in Scala 3This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/)has PRsam

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions