Skip to content

Commit 367d09a

Browse files
sjrdjulienrf
authored andcommitted
Update the binary compatibility guarantees for Scala 3.
In addition, be a more assertive that we strongly *intend* compatibility of produced artifacts to be preserved, while still making it clear that we cannot *guarantee* it as it is not mechanically checkable.
1 parent 7a9cb21 commit 367d09a

File tree

1 file changed

+59
-10
lines changed

1 file changed

+59
-10
lines changed

_overviews/core/binary-compatibility-of-scala-releases.md

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,80 @@ partof: binary-compatibility
77
permalink: /overviews/core/:title.html
88
---
99

10-
When two versions of Scala are binary compatible, it is safe to compile your project on one Scala version and link against another Scala version at run time. Safe run-time linkage (only!) means that the JVM does not throw a (subclass of) [`LinkageError`](https://docs.oracle.com/javase/7/docs/api/java/lang/LinkageError.html) when executing your program in the mixed scenario, assuming that none arise when compiling and running on the same version of Scala. Concretely, this means you may have external dependencies on your run-time classpath that use a different version of Scala than the one you're compiling with, as long as they're binary compatible. In other words, separate compilation on different binary compatible versions does not introduce problems compared to compiling and running everything on the same version of Scala.
10+
When two versions of Scala are binary compatible, it is safe to compile your project on one Scala version and link against another Scala version at run time. Safe run-time linkage (only!) means that the JVM does not throw a (subclass of) [`LinkageError`](https://docs.oracle.com/javase/8/docs/api/java/lang/LinkageError.html) when executing your program in the mixed scenario, assuming that none arise when compiling and running on the same version of Scala. Concretely, this means you may have external dependencies on your run-time classpath that use a different version of Scala than the one you're compiling with, as long as they're binary compatible. In other words, separate compilation on different binary compatible versions does not introduce problems compared to compiling and running everything on the same version of Scala.
1111

12-
We check binary compatibility automatically with [MiMa](https://github.com/lightbend/migration-manager). We strive to maintain a similar invariant for the `behavior` (as opposed to just linkage) of the standard library, but this is not checked mechanically (Scala is not a proof assistant so this is out of reach for its type system).
12+
We check binary compatibility automatically with [MiMa](https://github.com/lightbend/mima). We strive to maintain a similar invariant for the `behavior` (as opposed to just linkage) of the standard library, but this is not checked mechanically (Scala is not a proof assistant so this is out of reach for its type system).
13+
14+
Note that for Scala.js and Scala Native, binary compatibility issues result in errors at build time, as opposed to run-time exceptions.
15+
They happen during their respective "linking" phases: `{fast,full}LinkJS` for Scala.js and `nativeLink` for Scala Native.
1316

1417
#### Forward and Back
1518
We distinguish forward and backward compatibility (think of these as properties of a sequence of versions, not of an individual version). Maintaining backwards compatibility means code compiled on an older version will link with code compiled with newer ones. Forward compatibility allows you to compile on new versions and run on older ones.
1619

1720
Thus, backwards compatibility precludes the removal of (non-private) methods, as older versions could call them, not knowing they would be removed, whereas forwards compatibility disallows adding new (non-private) methods, because newer programs may come to depend on them, which would prevent them from running on older versions (private methods are exempted here as well, as their definition and call sites must be in the same compilation unit).
1821

19-
These are strict constraints, but they have worked well for us since Scala 2.10.x. They didn't stop us from fixing large numbers of issues in minor releases. The advantages are clear, so we will maintain this policy for future Scala major releases.
22+
#### Guarantees and Versioning
23+
For Scala 2, the *minor* version is the *third* number in a version, e.g., 10 in v2.13.10.
24+
The major version is the second number, which is 13 in our example.
25+
26+
Scala 2 guarantees both backward and forward compatibility across *minor* releases within a single major release.
27+
28+
These are strict constraints, but they have worked well for us since Scala 2.10.x.
29+
They didn't stop us from fixing large numbers of issues in minor releases.
30+
So far, that policy is still applicable for Scala 2, although [there exists a proposal to remove the forward compatibility guarantee](https://docs.scala-lang.org/sips/drop-stdlib-forwards-bin-compat.html).
31+
32+
For Scala 3, the minor version is the *second* number in a version, e.g., 2 in v3.2.1.
33+
The third number is the *patch* version.
34+
The major version is always 3.
35+
36+
Scala 3 guarantees both backward and forward compatibility across *patch* releases within a single minor release.
37+
In particular, this applies within an entire [Long-Term-Support (LTS) series](https://www.scala-lang.org/blog/2022/08/17/long-term-compatibility-plans.html) (which will start with Scala 3.3.x).
38+
39+
It also guarantees *backward* compatibility across *minor* releases in the entire 3.x series, but not forward compatibility.
40+
This means that libraries compiled with any Scala 3.x version can be used in projects compiled with any Scala 3.y version with y >= x.
41+
42+
In addition, Scala 3.x provides backward binary compatibility with respect to Scala 2.13.y.
43+
Libraries compiled with Scala 2.13.y can be used in projects using Scala 3.x.
44+
This policy does not apply to experimental Scala 2 features, which notably includes *macros*.
2045

21-
#### Meta
22-
Note that so far we've only talked about the jars generated by scalac for the standard library and reflection.
23-
Our policies do not extend to the meta-issue: ensuring binary compatibility for bytecode generated from identical sources, by different version of scalac? (The same problem exists for compiling on different JDKs.) While we strive to achieve this, it's not something we can test in general. Notable examples where we know meta-binary compatibility is hard to achieve: specialisation and the optimizer.
46+
In general, none of those guarantees apply to *experimental* features and APIs.
2447

25-
In short, we recommend that library authors use [MiMa](https://github.com/lightbend/migration-manager) to verify compatibility of minor versions before releasing.
26-
Compiling identical sources with different versions of the scala compiler (or on different JVM versions!) could result in binary incompatible bytecode. This is rare, and we try to avoid it, but we can't guarantee it will never happen.
48+
#### Checking
49+
For the Scala library artifacts (`scala-library`, `scala-reflect` and `scala3-library`), these guarantees are mechanically checked with [MiMa](https://github.com/lightbend/mima).
50+
51+
The *policies* above extend to libraries compiled by particular Scala compiler versions.
52+
Every effort is made to preserve the binary compatibility of artifacts produced by the compiler.
53+
*However*, that cannot be mechanically checked.
54+
It is therefore possible, due to bugs or unforeseen consequences, that recompiling a library with a different compiler version affects its binary API.
55+
We cannot *guarantee* that it will never happen.
56+
57+
We recommend that library authors use [MiMa](https://github.com/lightbend/mima) themselves to verify compatibility of minor versions before releasing.
58+
59+
#### TASTy and Pickle Compatibility
60+
*Binary* compatibility is a concept relevant at link time of the target platform (JVM, Scala.js or Scala Native).
61+
TASTy and Pickle compatibility are similar but apply at *compile* time for the Scala compiler.
62+
TASTy applies to Scala 3, Pickle to Scala 2.
63+
64+
If a library was compiled with an older version of the compiler, we say that the library is backward TASTy/Pickle compatible if it can be used within an application compiled with a newer compiler version.
65+
Likewise, forward TASTy/Pickle compatibility goes in the other direction.
66+
67+
The same policies as for binary compatibility apply to TASTy/Pickle compatibility, although they are not mechanically checked.
68+
69+
Library authors may automatically check TASTy/Pickle backward compatibility for their libraries using [TASTy-MiMa](https://github.com/scalacenter/tasty-mima).
70+
Disclaimer: TASTy-MiMa is a young project.
71+
At this point, you are likely going to run into bugs.
72+
Please report issues you find to its issue tracker.
2773

2874
#### Concretely
29-
We guarantee forwards and backwards compatibility of the `"org.scala-lang" % "scala-library" % "2.N.x"` and `"org.scala-lang" % "scala-reflect" % "2.N.x"` artifacts, except for
75+
We guarantee forwards and backwards compatibility of the `"org.scala-lang" % "scala-library" % "2.N.x"` and `"org.scala-lang" % "scala-reflect" % "2.N.x"` artifacts, except for
3076
- the `scala.reflect.internal` and `scala.reflect.io` packages, as scala-reflect is still experimental, and
3177
- the `scala.runtime` package, which contains classes used by generated code at runtime.
3278

3379
We also strongly discourage relying on the stability of `scala.concurrent.impl`, `scala.sys.process.*Impl`, and `scala.reflect.runtime`, though we will only break compatibility for severe bugs here.
3480

35-
Note that we will only enforce *backwards* binary compatibility for modules (artifacts under the groupId `org.scala-lang.modules`). As they are opt-in, it's less of a burden to require having the latest version on the classpath. (Without forward compatibility, the latest version of the artifact must be on the run-time classpath to avoid linkage errors.)
81+
We guarantee backward compatibility of the `"org.scala-lang" % "scala3-library_3" % "3.x.y"` artifact.
82+
Forward compatibility is only guaranteed for `3.N.y` within a given `N`.
83+
84+
We enforce *backward* (but not forward) binary compatibility for *modules* (artifacts under the groupId `org.scala-lang.modules`). As they are opt-in, it's less of a burden to require having the latest version on the classpath. (Without forward compatibility, the latest version of the artifact must be on the run-time classpath to avoid linkage errors.)
3685

3786
Finally, from Scala 2.11 until Scala 2.13.0-M1, `scala-library-all` aggregates all modules that constitute a Scala release. Note that this means it does not provide forward binary compatibility, whereas the core `scala-library` artifact does. We consider the versions of the modules that `"scala-library-all" % "2.N.x"` depends on to be the canonical ones, that are part of the official Scala distribution. (The distribution itself is defined by the `scala-dist` maven artifact.)

0 commit comments

Comments
 (0)