Compiler version
Scala 3.9.0-RC4 (also affects 3.9.0-RC3, 3.9.0-RC1)
Minimized code
MyTrait.scala:
object MyTrait:
case class Event[D](value: D)
case class State[S, D](name: S, data: D)
trait MyTrait[S, D]:
import MyTrait.*
type StateT = State[S, D]
type EventT = Event[D]
type StateFunction = PartialFunction[EventT, StateT]
private val handler: StateFunction = { case e => State(null.asInstanceOf[S], e.value) }
abstract class MyAbstract[S, D] extends MyTrait[S, D]
MyJavaClass.java:
public class MyJavaClass extends MyAbstract<String, String> {
}
Steps to reproduce
- Compile the Scala code:
scalac -d out MyTrait.scala
- Compile the Java class:
javac -cp out:$(cs fetch org.scala-lang:scala3-library_3:3.9.0-RC4 --classpath) MyJavaClass.java
Output
MyJavaClass.java:1: error: MyJavaClass is not abstract and does not override abstract method
MyTrait$_setter_$MyTrait$$handler_$eq(PartialFunction<Event<String>,State<String,String>>) in MyTrait
public class MyJavaClass extends MyAbstract<String, String> {
^
Expectation
The Java class should compile successfully, as it does with Scala 3.3.8.
Root cause analysis
This is a residual issue related to #26372 (fixed by #26374, regression fixed by #26542).
The fix in #26542 correctly handles cases where the trait type parameter is used directly in the field type. However, when the field type is expressed through type aliases defined inside the trait that expand to types containing the trait's type parameters, the mixed-in setter in the abstract class still gets Object types instead of the class's type variables.
Bytecode evidence (from Apache Pekko's FSM trait, same pattern):
Trait FSM[S, D] — abstract setter uses type variables:
Signature: (Lscala/PartialFunction<Lorg/apache/pekko/actor/FSM$Event<TD;>;Lorg/apache/pekko/actor/FSM$State<TS;TD;>;>;)V
Class AbstractFSM[S, D] — concrete setter uses Object:
Signature: (Lscala/PartialFunction<Lorg/apache/pekko/actor/FSM$Event<Ljava/lang/Object;>;Lorg/apache/pekko/actor/FSM$State<Ljava/lang/Object;Ljava/lang/Object;>;>;)V
The trait declares the abstract setter with TD/TS type variables, but the mixed-in concrete implementation in AbstractFSM widens them to Object. javac sees these as incompatible signatures and reports the abstract method as unimplemented.
Key difference from #26372: The field type uses trait-local type aliases (type StateFunction = PartialFunction[EventT, StateT] where EventT = Event[D], StateT = State[S, D]). The mixinGenericInfos logic in Mixin.scala appears to not substitute type parameters when they are reached through type alias expansion.
Workaround
None for library authors — the trait must be restructured to avoid type aliases in private val types, which is invasive.
Context
This blocks Apache Pekko's Scala 3.9.0-RC4 cross-compilation (apache/pekko#3303). The FSM[S, D] trait and PersistentFSMBase both use this pattern extensively.
Compiler version
Scala 3.9.0-RC4 (also affects 3.9.0-RC3, 3.9.0-RC1)
Minimized code
MyTrait.scala:
MyJavaClass.java:
Steps to reproduce
Output
Expectation
The Java class should compile successfully, as it does with Scala 3.3.8.
Root cause analysis
This is a residual issue related to #26372 (fixed by #26374, regression fixed by #26542).
The fix in #26542 correctly handles cases where the trait type parameter is used directly in the field type. However, when the field type is expressed through type aliases defined inside the trait that expand to types containing the trait's type parameters, the mixed-in setter in the abstract class still gets
Objecttypes instead of the class's type variables.Bytecode evidence (from Apache Pekko's
FSMtrait, same pattern):Trait
FSM[S, D]— abstract setter uses type variables:Class
AbstractFSM[S, D]— concrete setter usesObject:The trait declares the abstract setter with
TD/TStype variables, but the mixed-in concrete implementation inAbstractFSMwidens them toObject. javac sees these as incompatible signatures and reports the abstract method as unimplemented.Key difference from #26372: The field type uses trait-local type aliases (
type StateFunction = PartialFunction[EventT, StateT]whereEventT = Event[D],StateT = State[S, D]). ThemixinGenericInfoslogic inMixin.scalaappears to not substitute type parameters when they are reached through type alias expansion.Workaround
None for library authors — the trait must be restructured to avoid type aliases in private val types, which is invasive.
Context
This blocks Apache Pekko's Scala 3.9.0-RC4 cross-compilation (apache/pekko#3303). The
FSM[S, D]trait andPersistentFSMBaseboth use this pattern extensively.