From 8c36284e850bd924dea9b6d4ace3325689598fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=8E=E9=B8=A3?= Date: Thu, 18 Jun 2026 19:04:30 +0800 Subject: [PATCH 1/3] fix: Scala 3.9 context bound and trait field compatibility Motivation: Scala 3.9.0-RC1 changes context bound desugaring and trait field bytecode generation. ClassTag/LogSource explicit passing via method(args)(ClassTag(x)) breaks, and private val in traits generates abstract setters visible to Java subclasses. Modification: - Behaviors.scala: use implicit val ClassTag instead of explicit passing for monitor, transformMessages, and withMdc methods - Ops.scala: use implicit val LogSource instead of explicit passing for Log and LogWithMarker preStart methods - FSM.scala, PersistentFSMBase.scala: change private val handleEventDefault to private def to avoid abstract setter generation Result: ClassTag and LogSource context bound errors resolved. handleEventDefault setter issue partially mitigated (remaining setters tracked in scala/scala3#26372). Tests: - sbt "++3.9.0-RC1!; compile" resolves ClassTag/LogSource errors References: scala/scala3#26372 - trait setter generic signature regression --- .../pekko/actor/typed/javadsl/Behaviors.scala | 15 ++++++++++----- .../main/scala/org/apache/pekko/actor/FSM.scala | 2 +- .../pekko/persistence/fsm/PersistentFSMBase.scala | 2 +- .../org/apache/pekko/stream/impl/fusing/Ops.scala | 6 ++++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/javadsl/Behaviors.scala b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/javadsl/Behaviors.scala index bc3ee7b8a0c..d4425cd4a5e 100644 --- a/actor-typed/src/main/scala/org/apache/pekko/actor/typed/javadsl/Behaviors.scala +++ b/actor-typed/src/main/scala/org/apache/pekko/actor/typed/javadsl/Behaviors.scala @@ -234,8 +234,10 @@ object Behaviors { * @param monitor The messages will also be sent to this `ActorRef` * @param behavior The inner behavior that is decorated */ - def monitor[T](interceptMessageClass: Class[T], monitor: ActorRef[T], behavior: Behavior[T]): Behavior[T] = - scaladsl.Behaviors.monitor(monitor, behavior)(ClassTag(interceptMessageClass)) + def monitor[T](interceptMessageClass: Class[T], monitor: ActorRef[T], behavior: Behavior[T]): Behavior[T] = { + implicit val ct: ClassTag[T] = ClassTag(interceptMessageClass) + scaladsl.Behaviors.monitor(monitor, behavior) + } /** * Behavior decorator that logs all messages to the [[pekko.actor.typed.Behavior]] using the provided @@ -332,8 +334,10 @@ object Behaviors { def transformMessages[Outer, Inner]( interceptMessageClass: Class[Outer], behavior: Behavior[Inner], - selector: JFunction[PFBuilder[Outer, Inner], PFBuilder[Outer, Inner]]): Behavior[Outer] = - BehaviorImpl.transformMessages(behavior, selector.apply(new PFBuilder).build())(ClassTag(interceptMessageClass)) + selector: JFunction[PFBuilder[Outer, Inner], PFBuilder[Outer, Inner]]): Behavior[Outer] = { + implicit val ct: ClassTag[Outer] = ClassTag(interceptMessageClass) + BehaviorImpl.transformMessages(behavior, selector.apply(new PFBuilder).build()) + } /** * Support for scheduled `self` messages in an actor. @@ -413,7 +417,8 @@ object Behaviors { asScalaMap(mdcForMessage.apply(message)) } - WithMdcBehaviorInterceptor[T](asScalaMap(staticMdc), mdcForMessageFun, behavior)(ClassTag(interceptMessageClass)) + implicit val ct: ClassTag[T] = ClassTag(interceptMessageClass) + WithMdcBehaviorInterceptor[T](asScalaMap(staticMdc), mdcForMessageFun, behavior) } } diff --git a/actor/src/main/scala/org/apache/pekko/actor/FSM.scala b/actor/src/main/scala/org/apache/pekko/actor/FSM.scala index fd93a273b7c..6f33d7ca70c 100644 --- a/actor/src/main/scala/org/apache/pekko/actor/FSM.scala +++ b/actor/src/main/scala/org/apache/pekko/actor/FSM.scala @@ -763,7 +763,7 @@ trait FSM[S, D] extends Actor with Listeners with ActorLogging { /* * unhandled event handler */ - private val handleEventDefault: StateFunction = { + private def handleEventDefault: StateFunction = { case Event(value, _) => log.warning("unhandled event " + value + " in state " + stateName) stay() diff --git a/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala b/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala index 448f00ef52d..89abe7fbb27 100644 --- a/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala +++ b/persistence/src/main/scala/org/apache/pekko/persistence/fsm/PersistentFSMBase.scala @@ -466,7 +466,7 @@ trait PersistentFSMBase[S, D, E] extends Actor with Listeners with ActorLogging /* * unhandled event handler */ - private val handleEventDefault: StateFunction = { + private def handleEventDefault: StateFunction = { case Event(value, _) => log.warning("unhandled event " + value + " in state " + stateName) stay() diff --git a/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/Ops.scala b/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/Ops.scala index 3832ccf2695..50ad7523de3 100644 --- a/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/Ops.scala +++ b/stream/src/main/scala/org/apache/pekko/stream/impl/fusing/Ops.scala @@ -1527,7 +1527,8 @@ private[stream] object Collect { log = logAdapter match { case Some(l) => l case _ => - Logging(materializer.system, materializer)(fromMaterializer) + implicit val ls: LogSource[Materializer] = fromMaterializer + Logging(materializer.system, materializer) } } @@ -1647,7 +1648,8 @@ private[stream] object Collect { log = logAdapter match { case Some(l) => l case _ => - Logging.withMarker(materializer.system, materializer)(fromMaterializer) + implicit val ls: LogSource[Materializer] = fromMaterializer + Logging.withMarker(materializer.system, materializer) } } From 6c7b9d78ff9c569212fc87ad4c4bdb2b1255b842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=8E=E9=B8=A3?= Date: Thu, 18 Jun 2026 19:20:35 +0800 Subject: [PATCH 2/3] fix: make -Yfuture-lazy-vals conditional for Scala 3.9+ Motivation: Scala 3.9 removed the -Yfuture-lazy-vals compiler option as the behavior is now the default. Passing this flag causes compilation errors on Scala 3.9+. Modification: Conditionally add -Yfuture-lazy-vals only when Scala minor version < 9 using CrossVersion.partialVersion check. Result: Project compiles on both Scala 3.3.x (which needs the flag) and Scala 3.9+ (which has the behavior by default). --- project/PekkoBuild.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/PekkoBuild.scala b/project/PekkoBuild.scala index 887a7fda1e7..68efe084145 100644 --- a/project/PekkoBuild.scala +++ b/project/PekkoBuild.scala @@ -97,8 +97,8 @@ object PekkoBuild { "-feature", "-unchecked", // 'blessed' since 2.13.1 - "-language:higherKinds", - "-Yfuture-lazy-vals") + "-language:higherKinds") ++ + (if (CrossVersion.partialVersion(scalaVersion.value).exists(_._2 < 9)) Seq("-Yfuture-lazy-vals") else Seq.empty) } else { Seq( "-encoding", From 0cde1fba16478f2b3435ff54119effb89eb108dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=99=8E=E9=B8=A3?= Date: Thu, 18 Jun 2026 20:49:26 +0800 Subject: [PATCH 3/3] chore: apply scalafmt to project/PekkoBuild.scala Motivation: CI scalafmt check failed because project/PekkoBuild.scala was not formatted. Modification: Ran scalafmt on the build file. Result: scalafmtSbtCheck and Code is formatted CI checks should pass. Tests: Not run - formatting only --- project/PekkoBuild.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/PekkoBuild.scala b/project/PekkoBuild.scala index 68efe084145..4abf525c834 100644 --- a/project/PekkoBuild.scala +++ b/project/PekkoBuild.scala @@ -98,7 +98,7 @@ object PekkoBuild { "-unchecked", // 'blessed' since 2.13.1 "-language:higherKinds") ++ - (if (CrossVersion.partialVersion(scalaVersion.value).exists(_._2 < 9)) Seq("-Yfuture-lazy-vals") else Seq.empty) + (if (CrossVersion.partialVersion(scalaVersion.value).exists(_._2 < 9)) Seq("-Yfuture-lazy-vals") else Seq.empty) } else { Seq( "-encoding",