Skip to content

Conversation

@lihaoyi
Copy link
Contributor

@lihaoyi lihaoyi commented Nov 20, 2025

Fixes #24382

This PR re-implements the shading and classpath isolation first implemented in #23849, but lost in the churn of #24243. It implements both the original source-based shading of PPrint/Fansi/Sourcecode, with additional classloader-based isolation of REPL implementation classes to further avoid conflicts with libraries on the user classpath

  • repl contains the current implementation of the Scala3 REPL
  • repl-embedded is the assembly jar of repl, containing all the transitive classfiles, but moved under the dotty.isolated namespace.
  • repl-embedded contains three main un-isolated things:
    • scala.tools.repl.EmbeddedReplMain, which spawns an isolated classloader that knows how to load stuff from the dotty.isolated namespace, and uses that to instantiate a ReplDriver
    • The PPrint/Fansi/Sourcecode libraries, which are shaded manually to dotty.shaded.* via source code mangling but are not classloader-isolated from user code
    • scala-library.jar, which is guaranteed to be the same anyway

This approach allows anyone using the repl-embedded artifact to be confident there will not be classpath conflicts with their own classpath, since all the Scala compiler jars and transitive dependencies are isolated within a classloader and will not collide with user code. Furthermore, this allows us to begin using arbitrary third-party dependencies in the scala3-repl's implementation without them bleeding into user code inside or outside the REPL, with a few exceptions like PPrint/Fansi/Sourcecode below. Compared to the earlier implementation in #23849, this provides much stronger isolation from the enclosing classpath and avoids leaking implementation dependencies like coursierapi into the enclosing scope

The PPrint/Fansi/Sourcecode libraries cannot be fully isolated because they need to be present in the same classloader as user code in order for PPrint logic like case s: Seq[_] => to work correctly: if they were in a separate classloader, the Seq in PPrint would differ from the Seq in user code, and these type tests would all fail. And due to the REPL interrupt instrumentation implemented in #24194, the REPL-code-classloader will ~always be different from the external user code classloader, so the PPrint/Fansi/Sourcecode classes have to be in that classloader to make things work and the only way to make it work

Tested manually to verify that the REPL classes do not appear on the classpath

sbt scala3-compiler-bootstrapped-new/publishLocalBin scala3-library-bootstrapped-new/publishLocalBin scala-library-bootstrapped/publishLocalBin tasty-core-bootstrapped-new/publishLocalBin scala3-repl/publishLocalBin scala3-interfaces/publishLocalBin
sbt scala3-repl-embedded/publishLocalBin
java -cp $(cs fetch -p org.scala-lang:scala3-repl-embedded_3:3.8.1-RC1-bin-SNAPSHOT) scala.tools.repl.EmbeddedReplMain -cp $(cs fetch -p org.scala-lang:scala3-repl-embedded_3:3.8.1-RC1-bin-SNAPSHOT)
scala> 1 + 1
val res0: Int = 2

scala> dotty.tools.repl.Main
-- [E008] Not Found Error: -----------------------------------------------------
1 |dotty.tools.repl.Main
  |^^^^^^^^^^^^^^^^^^^^^
  |value Main is not a member of dotty.tools.repl
1 error found

scala> fansi.Str("")
-- [E006] Not Found Error: -----------------------------------------------------
1 |fansi.Str("")
  |^^^^^
  |Not found: fansi
  |
  | longer explanation available when compiling with `-explain`
1 error found

Note that both within the REPL and outside the REPL in the enclosing codebase, dotty.tools.repl.Main cannot be resolved. That is an improvement over the status quo where dotty.tools.repl.Main and it's dependencies like coursierapi could conflict with things on the user classpath

@lihaoyi lihaoyi changed the title Provided a repl-embedded artifact which shades everything to avoid conflicts [WIP] Provided a repl-embedded artifact which shades everything to avoid conflicts Nov 20, 2025
@lihaoyi lihaoyi changed the title [WIP] Provided a repl-embedded artifact which shades everything to avoid conflicts [WIP] Provided a repl-embedded artifact which shades and isolates everything to avoid conflicts Nov 21, 2025
@lihaoyi lihaoyi changed the title [WIP] Provided a repl-embedded artifact which shades and isolates everything to avoid conflicts Provided a repl-embedded artifact which shades and isolates everything to avoid conflicts Nov 22, 2025
@lihaoyi
Copy link
Contributor Author

lihaoyi commented Nov 22, 2025

This should be ready for review I think, CC @Gedochao since you seem to be the one who always assigns people

@Gedochao Gedochao added the stat:needs decision Some aspects of this issue need a decision from the maintainance team. label Nov 23, 2025
@Gedochao Gedochao requested a review from hamzaremmal November 23, 2025 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat:needs decision Some aspects of this issue need a decision from the maintainance team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-isolate PPrint + REPL extra libs

3 participants