-
-
Notifications
You must be signed in to change notification settings - Fork 414
Description
There is an open PR for sbt to implement SIP-51 (unfreezing the Scala library). The topic was discussed at a recent Scala meeting and will be brought up in the next SIP meeting (Friday Mar 15); everyone seems to be supporting it.
Example:
➜ m cat build.sc
import mill._, scalalib._
object proj extends ScalaModule {
def scalaVersion = "2.13.8"
def ivyDeps = Agg(
ivy"com.softwaremill.sttp.client3::core:3.8.3", // depends on scala-library 2.13.10, ws 1.3.10
ivy"com.softwaremill.sttp.shared::ws:1.2.7",
)
}
➜ m mill show proj.runClasspath
[
"qref:v1:868554b6:/.../maven2/com/softwaremill/sttp/client3/core_2.13/3.8.3/core_2.13-3.8.3.jar",
"qref:v1:f3ba6af6:/.../maven2/com/softwaremill/sttp/shared/ws_2.13/1.3.10/ws_2.13-1.3.10.jar",
"qref:v1:438104da:/.../maven2/org/scala-lang/scala-library/2.13.8/scala-library-2.13.8.jar",
...
]
ws
is upgraded to 1.3.10, but scala-library
remains at 2.13.8.
Constraints for Scala 2.13
In the example shown above, sbt willl fail the build and require the user to upgrade scalaVersion
to 2.13.10. The reason is interactions between the compiler's compile-time and run-time classpath.
On a high level, the compiler is invoked as java -cp RUNTIME_CLASSPATH scala.tool.nsc.Main -cp COMPILETIME_CLASSPATH Source.scala
. Both classpaths contain a scala-library.
- The scala-library on the runtime classpath needs to match the compiler version (also on the runtime classpath) exactly because compiler releases are built with the inliner enabled.
- The scala-library on the runtime classpath cannot be newer because
- When running the REPL (
mill console
), REPL lines compiled against a newer library could fail to execute on the older library - Macros from the classpath compiled against a newer library could fail to expand on the older library
- When running the REPL (
The implementation for Scala 2.13 in sbt is therefore
- unpin scala-library in dependency resolution
- fail the build if the Scala library is newer than the build's
scalaVersion
- use coursier's
SameVersion
rule to ensure scala-library, scala-reflect and scala-compiler are all at the same version (https://github.com/coursier/sbt-coursier/pull/490/files)- this is important for projects that have a library dependency on scala-reflect, for example. Because Scala is built with the inliner, the artifacts need to be at exactly the same version.
For sbt-specific details see the individual commit messages in https://github.com/sbt/sbt/pull/7480/commits.
Scala 3
For Scala 3, unpinning scala-library and updating it in dependency resolution like other libraries is the right solution. Currently mill pins scala-library
according to the Scala version:
➜ m cat build.sc
import mill._, scalalib._
object proj extends ScalaModule {
def scalaVersion = "3.2.0" // depends on scala-library 2.13.8
def ivyDeps = Agg(
ivy"com.softwaremill.sttp.client3::core:3.8.3", // depends on scala-library 2.13.10
)
}
➜ m mill show proj.runClasspath
[
"qref:v1:feb4b39a:/.../maven2/com/softwaremill/sttp/client3/core_3/3.8.3/core_3-3.8.3.jar",
"qref:v1:2d688157:/.../maven2/org/scala-lang/scala3-library_3/3.2.0/scala3-library_3-3.2.0.jar",
"qref:v1:438104da:/.../maven2/org/scala-lang/scala-library/2.13.8/scala-library-2.13.8.jar",
...
]
Just as in Scala 2, to support macros and the REPL correctly, the runtime classpath of the Scala 3 compiler needs to have the updated scala-library. A macro on the dependency classpath can be compiled against some new Scala library; expanding it in the compiler with an older Scala library can fail.
Here's a test case for this scenario (it exploits some package private addition that was done in 2.13.9, as an example of what will happen after unfreezing the standard library):
// A.scala, in project a, Scala version 3.2.2 (which depends on scala-library 2.13.10)
import scala.quoted.*
package scala.collection {
object Exp:
def m(i: Int) = IterableOnce.checkArraySizeWithinVMLimit(i) // added in 2.13.9
}
object Mac:
inline def inspect(inline x: Any): Any = ${ inspectCode('x) }
def inspectCode(x: Expr[Any])(using Quotes): Expr[Any] =
scala.collection.Exp.m(42)
x
// B.scala, project b, Scala version 3.2.0 (which uses scala-library 2.13.8)
@main def hubu =
println(scala.util.Properties.versionString)
Mac.inspect(println("hai"))
Scala 2.12
Nothing should change for projects using Scala 2.12 or older.