Skip to content

Fix Scala 3.8.4 compilation compatibility while preserving 2.13 and 3.3.x cross-build#3060

Closed
He-Pin wants to merge 2 commits into
apache:mainfrom
He-Pin:scala3-upgrade-fix-warnings
Closed

Fix Scala 3.8.4 compilation compatibility while preserving 2.13 and 3.3.x cross-build#3060
He-Pin wants to merge 2 commits into
apache:mainfrom
He-Pin:scala3-upgrade-fix-warnings

Conversation

@He-Pin

@He-Pin He-Pin commented Jun 13, 2026

Copy link
Copy Markdown
Member

Motivation

The codebase needs to compile with both Scala 3.3.x (current production) and Scala 3.8.4 (forward compatibility for the upcoming 3.9 LTS). Several patterns in the codebase break under Scala 3.8 due to changes in how context bounds are desugared (they become using clauses), stricter deprecation warnings, and syntax changes.

Modification

171 files changed across the codebase:

  • ClassTag context bound fixes: In Scala 3.8, [T: ClassTag] desugars to a using clause, making explicit ClassTag passing positional-incompatible. Fixed by using implicit val ct: ClassTag[T] = ClassTag(clazz) in scope and relying on implicit resolution instead of explicit passing. Key files: PersistencePlugin.scala, EventSourcedBehaviorTestKit.scala, ActorContextSpec.scala, BehaviorTestKitSpec.scala, Behavior.scala, EventStream.scala, Topic.scala, ShardingProducerController.scala, ShardedDaemonProcessImpl.scala, StreamTestKit.scala.

  • PersistencePlugin implicit parameter merge: Merged [T: ClassTag] context bound and (implicit ev: PluginProvider[...]) into a single implicit parameter list (implicit ct: ClassTag[T], ev: PluginProvider[T, ScalaDsl, JavaDsl]) to avoid context bound + separate implicit parameter mismatch in Scala 3.8.

  • .apply() trick: Used decoration[T].apply(behavior) instead of decoration[T](behavior) to separate implicit resolution from function application (Scala 2.13 interprets (behavior) as the implicit ClassTag argument).

  • private[this]private: Scala 3.8 deprecates private[this] in favor of private.

  • = _ initializers → explicit defaults: Replaced = _ with = null, = None, = 0, etc. as Scala 3.8 deprecates uninitialized vars.

  • SortedSet.empty(ordering) fix: Bound ordering as implicit val ord before calling .empty[Member] to avoid implicit resolution issues.

  • @unchecked annotations: Added for type-test patterns that can't be checked at runtime under Scala 3.

  • @nowarn filters: Used message-based @nowarn("msg=never used") instead of category-based filters for cross-version compatibility.

  • PekkoDisciplinePlugin.scala: Added -Wconf suppression rules for Scala 3.8 deprecation/syntax warnings in both main and test scopes.

  • No using keyword in shared sources: All changes use cross-compatible patterns (implicit val, .apply() trick). The using keyword only appears in scala-3/ directories.

Result

All modules compile with both Scala 2.13.18 and Scala 3.8.4. Production Scala 3 version remains at 3.3.8 — this change only ensures forward compatibility. No behavioral changes to the runtime.

Tests

  • sbt "++3.8.4" "Test / compile" — success across all modules
  • sbt "Test / compile" (Scala 2.13.18) — success across all modules

References

None - proactive Scala 3.8 forward compatibility

@He-Pin He-Pin force-pushed the scala3-upgrade-fix-warnings branch from 812a6d1 to 913415e Compare June 13, 2026 17:49
… cross-build

Motivation:
The codebase needs to compile with both Scala 3.3.x (current production) and
Scala 3.8.4 (forward compatibility for upcoming 3.9 LTS). Several patterns
valid in 3.3.x are errors or warnings in 3.8.4.

Modification:
- Merge PersistencePlugin [T: ClassTag] context bound and (implicit ev:
  PluginProvider) into a single implicit parameter list so subclasses can
  pass both arguments without the `using` keyword (cross-compatible with
  Scala 2.13 and 3.3.x)
- Replace explicit ClassTag passing `method(arg)(ClassTag(clazz))` with
  `implicit val ct = ClassTag(clazz); method(arg)` pattern throughout the
  codebase (context bounds become `using` clauses in Scala 3.8, making
  positional passing an error)
- Fix SortedSet.empty(ordering) / TreeSet.empty(ordering) across cluster,
  cluster-tools, cluster-sharding, distributed-data modules by binding the
  Ordering as an implicit val before calling .empty[Member]
- Add @unchecked to type-test patterns that cannot be checked at runtime
  (Reachability.scala, AskPattern.scala)
- Fix `_` wildcard type argument to `?` in scala-3-only source files
- Fix LogSource resolution in MultiNodeSpec (classOf instead of this.getClass)
- Fix "pure expression does nothing" in FixedBufferSpec structural type calls
- Remove discarded non-Unit value (unused Success) in SnapshotStorage
- Add -Wconf suppression rules for Scala 3.8 deprecation and syntax warnings
  in PekkoDisciplinePlugin (both main and test scopes)

Result:
All modules compile successfully with both Scala 2.13.18 and Scala 3.8.4.
Cross-compilation with Scala 2.13 is preserved. No `using` keyword is used
in shared source files.

Tests:
- sbt "++3.8.4" "Test / compile" — success
- sbt "Test / compile" (2.13) — success (docs module has pre-existing unused warnings)

References:
None - proactive Scala 3.8 forward compatibility
@He-Pin He-Pin force-pushed the scala3-upgrade-fix-warnings branch from 913415e to 462dff8 Compare June 13, 2026 18:19
@He-Pin He-Pin marked this pull request as draft June 13, 2026 18:22
@He-Pin He-Pin marked this pull request as ready for review June 13, 2026 18:28
@He-Pin He-Pin added this to the 2.0.0-M4 milestone Jun 13, 2026
@He-Pin He-Pin requested review from mdedetrich and pjfanning June 13, 2026 18:41
He-Pin added a commit that referenced this pull request Jun 13, 2026
…ations

Motivation:
The initial Scala 3.8 compatibility changes had some code duplication,
unnecessary type casts, and could be simplified in several places.

Modification:
- Extract shared isScala3_8Plus method in JdkOptions to eliminate
  duplication with Jdk9.scala
- Re-add Scala 3.8 -Wconf suppressions in PekkoDisciplinePlugin with
  deduplication using shared scala3Suppressions/scala3DocSuppressions
- Simplify BehaviorTestKitSpec messageAdapter by removing unnecessary
  asInstanceOf and using proper ClassTag[U] type parameter
- Simplify ActorContextSpec ClassTag.Null passing by using explicit
  parameter instead of implicit val with @nowarn

Result:
Cleaner, more maintainable Scala 3.8 compatibility code with less
duplication and unnecessary type casts.

Tests:
Not run - code simplifications only, no behavior changes

References:
Refs #3060
He-Pin added a commit that referenced this pull request Jun 13, 2026
…estKit

Motivation:
PR #3060 CI was failing with two categories of errors on Scala 2.13:
1. ~100 fatal warnings in docs/Test/compile from unused imports, locals,
   and pattern variables that were not suppressed
2. "not found: type U" in BehaviorTestKitSpec due to type erasure in
   pattern matching

Modification:
- PekkoDisciplinePlugin: add docsScala2Suppressions with explicit -Wconf
  rules for each unused subcategory (unused-imports, unused-locals,
  unused-pat-vars, etc.) because Scala 2.13's -Wconf uses exact string
  matching, so cat=unused:s does not match subcategories
- BehaviorTestKitSpec: revert ClassTag[U] change back to explicit
  ClassTag(messageClass) passing since U is not in scope during
  pattern matching of CreateMessageAdapter

Result:
docs/Test/compile and actor-testkit-typed/Test/compile pass on both
Scala 2.13.18 and 3.3.8

Tests:
- sbt "++ 2.13.18 docs/Test/compile" — success (30 compilation units)
- sbt "++ 3.3.8 docs/Test/compile" — success
- sbt "++ 2.13.18 actor-testkit-typed/Test/compile" — success
- sbt "++ 3.3.8 actor-testkit-typed/Test/compile" — success

References:
Fixes CI failures on PR #3060
@He-Pin He-Pin removed this from the 2.0.0-M4 milestone Jun 13, 2026
@He-Pin He-Pin marked this pull request as draft June 13, 2026 22:13
…docs Test compilation

Motivation:
The docs module Test compilation fails on Scala 2.13 CI with unused-imports,
unused-params, unused-pat-vars, and unused-locals warnings treated as errors.
The PR split -Wconf rules from a single comma-separated string into separate
-Wconf:xxx scalacOptions entries. When split, the -Wconf:any:e catch-all
overrides the category-specific suppressions due to Scala 2.13's -Wconf
first-match-wins semantics across separate compiler options.

Modification:
Merge the split -Wconf options back into single comma-separated strings in
defaultScalaOptions, docs Compile overrides, and docs Test overrides. This
restores the original -Wconf rule evaluation order where category-specific
suppressions (cat=unused:s, cat=deprecation:s, cat=unchecked:s) take effect
before the any:e catch-all within the same compiler option.

Result:
docs/Test compiles successfully with both Scala 2.13.18 and Scala 3.3.8.
All core modules (actor, actor-typed, persistence, cluster) compile with
Scala 2.13. The Scala 3.8 forward compatibility changes remain intact.

Tests:
- sbt "docs / Test / compile" (Scala 2.13.18) — success
- sbt "++3.3.8" "docs / Test / compile" — success
- sbt "actor/Test/compile" "actor-typed/Test/compile" "persistence/Test/compile" "cluster/Test/compile" (2.13) — success

References:
Refs apache#3060
@He-Pin He-Pin marked this pull request as ready for review June 14, 2026 09:42
@He-Pin He-Pin added this to the 2.0.0-M4 milestone Jun 14, 2026
case CreateMessageAdapter(messageClass, f, replyTo) =>
val adaptor = context.messageAdapter(f)(ClassTag(messageClass))
implicit val ct: ClassTag[Any] = ClassTag(messageClass)
val adaptor = context.messageAdapter[Any](f.asInstanceOf[Any => Command])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's ok to make this change but the casting seems weird and could be a bug in scala 3.8

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the asInstanceOf cast was a workaround for Scala 3.8 context bound desugaring. It has been reverted to the original context.messageAdapter(f)(ClassTag(messageClass)) pattern in commit 683f874 — the explicit ClassTag passing still works correctly in Scala 3.8 since using parameters can be passed positionally. The refactored ClassTag[U] version did not compile on Scala 2.13 because U is erased in pattern matching.

@He-Pin

He-Pin commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

The 3.9.0-RC1 has just been released, hope this will make the code compiles with 3.9.0-RC1

@He-Pin

He-Pin commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

Closing to reopen with a rebased branch that resolves merge conflicts with main (Hub.scala private[this] changes from #3063).

@He-Pin He-Pin closed this Jun 14, 2026
@He-Pin He-Pin deleted the scala3-upgrade-fix-warnings branch June 14, 2026 11:40
@pjfanning pjfanning removed this from the 2.0.0-M4 milestone Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants