This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-http.git


The following commit(s) were added to refs/heads/main by this push:
     new 4784d0bf5 fix: Scala 3.9 compatibility fixes (#1074)
4784d0bf5 is described below

commit 4784d0bf5654f7d871efd74302436e61b4ff25ad
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Fri Jun 19 17:22:14 2026 +0800

    fix: Scala 3.9 compatibility fixes (#1074)
    
    * build: fix sbt lint warnings by excluding unused keys
    
    Motivation:
    sbt lint reported 45 unused keys across sub-projects.
    
    Modification:
    Add projectInfoVersion, mimaReportSignatureProblems, autoAPIMappings,
    and unidocProjectFilter to excludeLintKeys in project/Common.scala
    globalSettings.
    
    Result:
    sbt lint warnings resolved.
    
    Tests:
    - sbt "http-core / compile; http / compile; parsing / compile" - sbt lint 
warnings resolved
    - sbt "http-caching / compile; http-cors / compile; http-testkit / compile" 
- sbt lint warnings resolved
    
    References:
    None - build configuration cleanup
    
    * fix: Scala 3.9 compatibility fixes
    
    Motivation:
    Scala 3.9.0-RC1 introduces several breaking changes:
    1. -language:_ wildcard option is no longer accepted
    2. -Yfuture-lazy-vals option removed (now default behavior)
    3. -Ywarn-dead-code is Scala 2 only
    4. Context bound desugaring changes, breaking explicit ClassTag/Tuple/
       LogSource passing patterns
    
    Modification:
    - build.sbt: make -language:_ conditional on Scala 3 < 3.9
    - project/Common.scala: move -Ywarn-dead-code to Scala 2 only,
      conditionally include -Yfuture-lazy-vals for Scala 3 < 3.9
    - HttpMessage.scala, RejectionHandler.scala, 
PredefinedToResponseMarshallers.scala:
      use implicit val ClassTag instead of context bound [T: ClassTag]
    - Directive.scala, BasicDirectives.scala, PathMatcher.scala:
      use implicit val Tuple instead of context bound [L: Tuple]
    - PoolInterface.scala, StageLoggingWithOverride.scala:
      use implicit val LogSource instead of explicit passing
    
    Result:
    pekko-http parsing, http-core, http, and http-testkit modules compile
    cleanly with Scala 3.9.0-RC1.
    
    Tests:
    - sbt "++3.9.0-RC1!; parsing/compile; http-core/compile; http/compile;
      http-testkit/compile" passes
    
    References: None - Scala 3.9 forward compatibility
    
    * refactor: extract shared notOnScala39Plus helper for scalacOptions
    
    Motivation:
    The same scalacOptions block for `-language:_` (excluded on Scala 3.9+)
    was repeated inline in 3 places in build.sbt.
    
    Modification:
    Add `Common.notOnScala39Plus` helper and replace all 3 inline blocks.
    
    Result:
    Single source of truth for the Scala 3.9+ language option exclusion.
    
    Tests:
    Not run - build config only
    
    References:
    Refs apache/pekko-http#1074
    
    * fix: qualify notOnScala39Plus with Common. and apply scalafmt
    
    Motivation:
    The previous commit used unqualified `notOnScala39Plus` which is not
    in scope in build.sbt, causing compilation failure.
    
    Modification:
    Prefix all 3 call sites with `Common.` and run scalafmt.
    
    Result:
    build.sbt compiles successfully with the shared helper.
    
    Tests:
    Not run - build config fix
    
    References:
    Refs apache/pekko-http#1074
---
 build.sbt                                                 |  6 +++---
 .../pekko/http/impl/engine/client/PoolInterface.scala     |  3 ++-
 .../pekko/http/impl/util/StageLoggingWithOverride.scala   |  3 ++-
 .../apache/pekko/http/scaladsl/model/HttpMessage.scala    |  6 +++---
 .../marshalling/PredefinedToResponseMarshallers.scala     |  3 ++-
 .../org/apache/pekko/http/scaladsl/server/Directive.scala |  9 +++++----
 .../apache/pekko/http/scaladsl/server/PathMatcher.scala   |  8 ++++----
 .../pekko/http/scaladsl/server/RejectionHandler.scala     |  4 ++--
 .../http/scaladsl/server/directives/BasicDirectives.scala |  2 +-
 project/Common.scala                                      | 15 ++++++++++++---
 10 files changed, 36 insertions(+), 23 deletions(-)

diff --git a/build.sbt b/build.sbt
index bed8bb90c..260676678 100644
--- a/build.sbt
+++ b/build.sbt
@@ -108,7 +108,7 @@ lazy val parsing = project("parsing")
   .settings(AutomaticModuleName.settings("pekko.http.parsing"))
   .addPekkoModuleDependency("pekko-actor", "provided", 
PekkoCoreDependency.default)
   .settings(Dependencies.parsing)
-  .settings(scalacOptions += "-language:_")
+  .settings(scalacOptions ++= 
Common.notOnScala39Plus(Seq("-language:_")).value)
   .settings(scalaMacroSupport)
   .enablePlugins(ScaladocNoVerificationOfDiagrams)
   .enablePlugins(ReproducibleBuildsPlugin)
@@ -136,7 +136,7 @@ lazy val http = project("http")
   .addPekkoModuleDependency("pekko-stream", "provided", 
PekkoCoreDependency.default)
   .settings(Dependencies.http)
   .settings(
-    Compile / scalacOptions += "-language:_")
+    Compile / scalacOptions ++= 
Common.notOnScala39Plus(Seq("-language:_")).value)
   .settings(scalaMacroSupport)
   .enablePlugins(BootstrapGenjavadoc, BoilerplatePlugin)
   .enablePlugins(ReproducibleBuildsPlugin)
@@ -191,7 +191,7 @@ lazy val httpTestkit = project("http-testkit")
   .settings(
     // don't ignore Suites which is the default for the junit-interface
     testOptions += Tests.Argument(TestFrameworks.JUnit, "--ignore-runners="),
-    Compile / scalacOptions ++= Seq("-language:_"),
+    Compile / scalacOptions ++= 
Common.notOnScala39Plus(Seq("-language:_")).value,
     Test / run / mainClass := 
Some("org.apache.pekko.http.javadsl.SimpleServerApp"))
   .enablePlugins(BootstrapGenjavadoc, MultiNodeScalaTest, 
ScaladocNoVerificationOfDiagrams)
   .enablePlugins(ReproducibleBuildsPlugin)
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
index 837fd3c2e..107aeab04 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
@@ -67,7 +67,8 @@ private[http] object PoolInterface {
     import hcps._
     import setup.{ connectionContext, settings }
     implicit val system = fm.system
-    val log: LoggingAdapter = Logging(system, poolId)(PoolLogSource)
+    implicit val logSource: LogSource[PoolId] = PoolLogSource
+    val log: LoggingAdapter = Logging(system, poolId)
 
     log.debug("Creating pool.")
 
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
index 731d1e7f4..2ac4084e5 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
@@ -42,7 +42,8 @@ private[pekko] trait StageLoggingWithOverride extends 
GraphStageLogic {
         _log =
           logOverride match {
             case DefaultNoLogging =>
-              pekko.event.Logging(materializer.system, 
logSource)(LogSource.fromClass)
+              implicit val ls: LogSource[Class[?]] = LogSource.fromClass
+              pekko.event.Logging(materializer.system, logSource)
             case x => x
           }
       case _ =>
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
index 97e6f916b..cc6d4fd45 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
@@ -136,8 +136,8 @@ sealed trait HttpMessage extends jm.HttpMessage {
   }
 
   /** Returns the first header of the given type if there is one */
-  def header[T >: Null <: jm.HttpHeader: ClassTag]: Option[T] = {
-    val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]]
+  def header[T >: Null <: jm.HttpHeader](implicit ct: ClassTag[T]): Option[T] 
= {
+    val clazz = ct.runtimeClass.asInstanceOf[Class[T]]
     HttpHeader.fastFind[T](clazz, headers) match {
       case OptionVal.Some(h)                     => Some(h)
       case _ if clazz == classOf[`Content-Type`] => 
Some(`Content-Type`(entity.contentType)).asInstanceOf[Option[T]]
@@ -146,7 +146,7 @@ sealed trait HttpMessage extends jm.HttpMessage {
   }
 
   /** Returns all the headers of the given type * */
-  def headers[T <: jm.HttpHeader: ClassTag]: immutable.Seq[T] = 
headers.collect {
+  def headers[T <: jm.HttpHeader](implicit ct: ClassTag[T]): immutable.Seq[T] 
= headers.collect {
     case h: T => h
   }
 
diff --git 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
index 9287678cc..8f4ffc698 100755
--- 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
+++ 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
@@ -92,7 +92,8 @@ trait PredefinedToResponseMarshallers extends 
LowPriorityToResponseMarshallerImp
         }
     })
 
-  implicit def fromEntityStreamingSupportAndByteStringMarshaller[T: ClassTag, 
M](implicit s: EntityStreamingSupport,
+  implicit def fromEntityStreamingSupportAndByteStringMarshaller[T, 
M](implicit ct: ClassTag[T],
+      s: EntityStreamingSupport,
       m: ToByteStringMarshaller[T]): ToResponseMarshaller[Source[T, M]] =
     fromEntityStreamingSupportAndByteStringSourceMarshaller(s, 
m.map(Source.single))
 }
diff --git 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
index 2e5493da7..7b3a801ea 100644
--- a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
+++ b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
@@ -99,7 +99,7 @@ abstract class Directive[L](implicit val ev: Tuple[L]) {
   /**
    * Flatmaps this directive using the given function.
    */
-  def tflatMap[R: Tuple](f: L => Directive[R]): Directive[R] =
+  def tflatMap[R](f: L => Directive[R])(implicit ev: Tuple[R]): Directive[R] =
     Directive[R] { inner => tapply { values => f(values).tapply(inner) } }
 
   /**
@@ -135,7 +135,7 @@ abstract class Directive[L](implicit val ev: Tuple[L]) {
    * Creates a new directive that is able to recover from rejections that were 
produced by `this` Directive
    * **before the inner route was applied**.
    */
-  def recover[R >: L: Tuple](recovery: immutable.Seq[Rejection] => 
Directive[R]): Directive[R] =
+  def recover[R >: L](recovery: immutable.Seq[Rejection] => 
Directive[R])(implicit ev: Tuple[R]): Directive[R] =
     Directive[R] { inner => ctx =>
       import ctx.executionContext
       @volatile var rejectedFromInnerRoute = false
@@ -148,7 +148,8 @@ abstract class Directive[L](implicit val ev: Tuple[L]) {
   /**
    * Variant of `recover` that only recovers from rejections handled by the 
given PartialFunction.
    */
-  def recoverPF[R >: L: Tuple](recovery: 
PartialFunction[immutable.Seq[Rejection], Directive[R]]): Directive[R] =
+  def recoverPF[R >: L](recovery: PartialFunction[immutable.Seq[Rejection], 
Directive[R]])(
+      implicit ev: Tuple[R]): Directive[R] =
     recover { rejections =>
       recovery.applyOrElse(rejections, (rejs: Seq[Rejection]) => 
RouteDirectives.reject(rejs: _*))
     }
@@ -162,7 +163,7 @@ object Directive {
   /**
    * Constructs a directive from a function literal.
    */
-  def apply[T: Tuple](f: (T => Route) => Route): Directive[T] =
+  def apply[T](f: (T => Route) => Route)(implicit ev: Tuple[T]): Directive[T] =
     new Directive[T] { def tapply(inner: T => Route) = f(inner) }
 
   /**
diff --git 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
index e1e5fd1f8..147d22fd9 100644
--- 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
+++ 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
@@ -156,20 +156,20 @@ object PathMatcher extends 
ImplicitPathMatcherConstruction {
   /**
    * Creates a PathMatcher that always matches, consumes nothing and extracts 
the given Tuple of values.
    */
-  def provide[L: Tuple](extractions: L): PathMatcher[L] =
+  def provide[L](extractions: L)(implicit ev: Tuple[L]): PathMatcher[L] =
     new PathMatcher[L] {
-      def apply(path: Path) = Matched(path, extractions)(ev)
+      def apply(path: Path) = Matched(path, extractions)
     }
 
   /**
    * Creates a PathMatcher that matches and consumes the given path prefix and 
extracts the given list of extractions.
    * If the given prefix is empty the returned PathMatcher matches always and 
consumes nothing.
    */
-  def apply[L: Tuple](prefix: Path, extractions: L): PathMatcher[L] =
+  def apply[L](prefix: Path, extractions: L)(implicit ev: Tuple[L]): 
PathMatcher[L] =
     if (prefix.isEmpty) provide(extractions)
     else new PathMatcher[L] {
       def apply(path: Path) =
-        if (path.startsWith(prefix)) Matched(path.dropChars(prefix.charCount), 
extractions)(ev)
+        if (path.startsWith(prefix)) Matched(path.dropChars(prefix.charCount), 
extractions)
         else Unmatched
     }
 
diff --git 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
index f8b4051c5..7f82d742e 100644
--- 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
+++ 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
@@ -90,8 +90,8 @@ object RejectionHandler {
      * Handles several Rejections of the same type at the same time.
      * The seq passed to the given function is guaranteed to be non-empty.
      */
-    def handleAll[T <: Rejection: ClassTag](f: immutable.Seq[T] => Route): 
this.type = {
-      val runtimeClass = implicitly[ClassTag[T]].runtimeClass
+    def handleAll[T <: Rejection](f: immutable.Seq[T] => Route)(implicit ct: 
ClassTag[T]): this.type = {
+      val runtimeClass = ct.runtimeClass
       cases += TypeHandler[T](runtimeClass, f)
       this
     }
diff --git 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
index 6d44a607f..c39c7a8d4 100644
--- 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
+++ 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
@@ -152,7 +152,7 @@ trait BasicDirectives {
    *
    * @group basic
    */
-  def tprovide[L: Tuple](values: L): Directive[L] =
+  def tprovide[L](values: L)(implicit ev: Tuple[L]): Directive[L] =
     Directive { _(values) }
 
   /**
diff --git a/project/Common.scala b/project/Common.scala
index ad9c54820..006807d7d 100644
--- a/project/Common.scala
+++ b/project/Common.scala
@@ -31,19 +31,19 @@ object Common extends AutoPlugin {
       "-deprecation",
       "-encoding", "UTF-8", // yes, this is 2 args
       "-unchecked",
-      "-Ywarn-dead-code",
       "-Wconf:msg=reached max recursion depth:s",
       "-Wconf:msg=Prefer the Scala annotation over Java's `@Deprecated`:s",
       "-release:" + javacTarget),
     scalacOptions ++= onlyOnScala2(Seq(
       "-Xlint",
+      "-Ywarn-dead-code",
       // Exhaustivity checking is only useful for simple sealed hierarchies 
and matches without filters.
       // In all other cases, the warning is non-actionable: you get spurious 
warnings that need to be suppressed
       // verbosely. So, opt out of those in general.
       "-Wconf:cat=other-match-analysis&msg=match may not be 
exhaustive:s")).value,
     scalacOptions ++= onlyOnScala3(Seq(
-      "-Wconf:cat=deprecation:s",
-      "-Yfuture-lazy-vals")).value,
+      "-Wconf:cat=deprecation:s")).value,
+    scalacOptions ++= onlyOnScala3Below39(Seq("-Yfuture-lazy-vals")).value,
     javacOptions ++=
       Seq("-encoding", "UTF-8", "--release", javacTarget),
     mimaReportSignatureProblems := true,
@@ -56,6 +56,15 @@ object Common extends AutoPlugin {
   def onlyOnScala3[T](values: Seq[T]): Def.Initialize[Seq[T]] = Def.setting {
     if (scalaVersion.value.startsWith("3")) values else Seq.empty[T]
   }
+  def onlyOnScala3Below39[T](values: Seq[T]): Def.Initialize[Seq[T]] = 
Def.setting {
+    if (scalaVersion.value.startsWith("3") && 
CrossVersion.partialVersion(scalaVersion.value).exists(_._2 < 9)) values
+    else Seq.empty[T]
+  }
+  def notOnScala39Plus[T](values: Seq[T]): Def.Initialize[Seq[T]] = 
Def.setting {
+    if (scalaVersion.value.startsWith("3") && 
CrossVersion.partialVersion(scalaVersion.value).exists(_._2 >= 9))
+      Seq.empty[T]
+    else values
+  }
 
   def scalaMinorVersion: Def.Initialize[Long] = Def.setting { 
CrossVersion.partialVersion(scalaVersion.value).get._2 }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to