Skip to content

Commit 21f9c1e

Browse files
committed
Inital draft of @binaryCompatible proposal.
1 parent 53fd83d commit 21f9c1e

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
layout: sip
3+
title: SIP XX - Improving binary compatibility with @binaryCompatible
4+
disqus: true
5+
---
6+
7+
__Dmitry Petrashko__
8+
9+
__first submitted 13 February 2017__
10+
11+
## Introduction ##
12+
13+
Scala is a language which evolves fast and thus made a decision to only promise binary compatibility across minor releases.
14+
At the same time, there is a demand to develop APIs that live longer than a major release cycle of Scala.
15+
This SIP introduces an annotation `@binaryCompatible` that checks that `what you write is what you get`.
16+
It will fail compilation in case emitted methods or their signatures
17+
are different from those written by users.
18+
As long as signatures of methods in source is not changed, `@binaryCompatible` annotated class
19+
will be compatible across major version of Scala.
20+
21+
## Use Cases
22+
Dotty currently uses java defined interfaces as public API for SBT in order to ensure binary compatibility.
23+
This interfaces can be replaced by `@binaryCompatible` annotated traits to reach the same goal.
24+
25+
26+
## Design guideline
27+
`@binaryCompatible` is a feature which is supposed to be used by a small subset of ecosystem to be binary compatible across major versions of Scala.
28+
Thus this is designed as an advanced feature that is used rarely and thus is intentionally verbose.
29+
It's designed to provide strong guarantees, in some cases sacrificing ease of use.
30+
31+
## Overview ##
32+
In order for a class or a trait to succeed compilation with `@binaryCompatible` annotation it has to be:
33+
- defined on the top level;
34+
- use a subset of Scala that during compilation does not require changes to public api of the class, including
35+
- synthesizing new members, either concrete or abstract;
36+
- changing binary signatures of existing members, either concrete or abstract;
37+
The `@binaryCompatible` does not change the compilation scheme of a class:
38+
compiling a class previously annotated by `@binaryCompatible`, will produce the same bytecode with or without `@binaryCompatible` annotation.
39+
40+
Below are several examples of classes and traits that succeed compilation with `@binaryCompatible`
41+
```scala
42+
{% highlight scala %}
43+
@binaryCompatible
44+
trait AbstractFile {
45+
/** @return The name of this file, note that two files may have the same name. */
46+
def name(): String
47+
48+
/** @return The path of this file, this might be a virtual path of an unspecified format. */
49+
def path(): String
50+
51+
/** @return If this is a real file on disk, a `java.io.File` that corresponds to this file.
52+
* Otherwise, an empty `Optional`.
53+
*/
54+
def jfile(): Optional[File]
55+
}
56+
57+
@binaryCompatible
58+
trait SourceFile extends AbstractFile {
59+
/** @return The content of this file as seen by the compiler. */
60+
def content(): Array[Char]
61+
}
62+
63+
@binaryCompatible
64+
trait Diagnostic {
65+
def message(): String
66+
67+
def level(): Int
68+
69+
def position(): Optional[SourcePosition]
70+
}
71+
72+
@binaryCompatible
73+
object Diagnostic {
74+
@static final val ERROR: Int = 2
75+
@static final val WARNING: Int = 1
76+
@static final val INFO: Int = 0
77+
}
78+
79+
{% endhighlight %}
80+
```
81+
82+
## Features that will fail compilation with `@binaryCompatible`
83+
84+
- lazy vals
85+
- default methods
86+
- default arguments
87+
- case classes
88+
- public fields
89+
- constant types(both explicit and inferred)
90+
- inline
91+
92+
93+
## `@binaryCompatible` and Scala.js
94+
95+
Allowing to write API-defining classes in Scala instead of Java will allow to compile them with Scala.js,
96+
which would have benefit of sharing the same source for two ecosystems.
97+
98+
Scala.js currently is binary compatible as long as original bytecode compiled by Scala JVM is binary compatible.
99+
Providing stronger binary compatibility guarantees for JVM will automatically provide stronger guarantees for Scala.js.
100+
101+
102+
## Comparison with MiMa ##
103+
The Migration Manager for Scala (MiMa in short) is a tool for diagnosing binary incompatibilities for Scala libraries.
104+
MiMa allows to compare binary APIs of two already compiled classes and reports errors if APIs do not match perfectly.
105+
106+
MiMa and `@binaryCompatible` complement each other, as `@binaryCompatible` helps to develop APIs that stay compatible
107+
across major versions, while MiMa checks that previously published artifacts indeed use the same API.
108+
109+
`@binaryCompatible` does not compare the currently compiled class or trait against previous version,
110+
so introduction of new members won't be prohibited. This is a use-case for MiMa.
111+
112+
MiMa does not indicate how hard, if possible, would it be to maintain compatibility of a class across future versions of Scala.
113+
Multiple features of Scala, most notably lazy vals and traits, has been compiled diffently by different Scala versions
114+
making porting existing compiled bytecode across versions very hard.
115+
MiMa would complain retroactively that the new version is incompatible to the old one, while `@binaryCompatible` would provide early notification.
116+
117+
## Compilation scheme ##
118+
No modification of typer or any existing phase is planned. The current proposed scheme introduces a late phase that runs before the very bytecode emission that checks that:
119+
- classes and traits annotated as `@binaryCompatible` are on the top level;
120+
- no non-private members where introduced inside classes and traits annotated as `@binaryCompatible` by compiler using phase travel;
121+
- no non-private members inside classes and traits annotated as `@binaryCompatible` has changed their signature from the one written by developer.
122+
123+
The current prototype is implemented for Dotty and supports everything descibed in this SIP.
124+
The implementation is simple with less than 50 lines of non-boilerplate code.
125+
The current implementation has a scope for improvement of error messages that will report domain specific details for disallowed features, but it already prohibits them.
126+
## See Also ##
127+
* [dotty#1900](https://github.com/lampepfl/dotty/pull/1900) is implementation for Dotty

0 commit comments

Comments
 (0)