You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This proposal introduces the `@binaryAPI` and `@binaryAPIAccessor` annotations on term definitions. The purpose of binary APIs is to have publicly accessible definitions in generated bytecode for definitions that are private or protected.
20
+
The purpose of binary APIs is to have publicly accessible definitions in generated bytecode for definitions that are package private or protected.
21
+
This proposal introduces the `@binaryAPI` annotation on term definitions and the `-WunstableInlineAccessors` linting flag.
20
22
21
23
22
24
## Motivation
@@ -66,7 +68,7 @@ object C:
66
68
67
69
### High-level overview
68
70
69
-
This proposal introduces 2 the `@binaryAPI`and `@binaryAPIAccessor` annotations, and adds a migration path to inline methods.
71
+
This proposal introduces the `@binaryAPI`annotation, and adds a migration path to inline methods in libraries (requiring binary compatibility).
70
72
71
73
#### `@binaryAPI` annotation
72
74
@@ -100,49 +102,18 @@ public class C {
100
102
In the bytecode, `@binaryAPI` definitions will have the [ACC_PUBLIC](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1-200-E.1) flag.
101
103
<!-- We can also set the [ACC_SYNTHETIC](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1-200-E.1) to hide these definitions from javac and java IDEs. -->
102
104
103
-
#### `@binaryAPIAccessor` annotation
104
-
105
-
A binary API with accessor is a definition that is annotated with `@binaryAPIAccessor`.
106
-
This annotation can be placed on `def`, `val`, `lazy val`, `var`, `object`, and `given` definitions.
107
-
The annotated definition will get a public accessor.
108
-
109
-
This can be used to access `private`/`private[this]` definitions within inline definitions.
110
-
111
-
Example:
112
-
~~~scala
113
-
classC {
114
-
@binaryAPIAccessor privatedefprivateAPI:Int= ...
115
-
@binaryAPIAccessor defpublicAPI:Int= ...
116
-
}
117
-
~~~
118
-
will generate the following bytecode signatures
119
-
~~~java
120
-
publicclassC {
121
-
publicC();
122
-
privateintprivateAPI();
123
-
publicintpublicAPI();
124
-
publicfinal int C$$inline$privateAPI();
125
-
publicfinal int C$$inline$publicAPI();
126
-
}
127
-
~~~
128
-
129
-
Note that the change from `private[this]` to package private, protected or public is a binary compatible change.
130
-
Removing this annotation is a binary incompatible change.
131
-
132
-
In the bytecode, `@binaryAPIAccessor` generated accessors will have the [ACC_PUBLIC | ACC_SYNTHETIC](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1-200-E.1) flag.
133
-
134
105
#### Binary API and inlining
135
106
136
107
A non-public reference in an inline method is handled as follows:
137
108
- if the reference is a `@binaryAPI` the reference is used;
138
-
- if the reference is a `@binaryAPIAccessor` the accessor is used;
139
-
- otherwise, an accessor is automatically generated and used. We will emit a warning with an actionable diagnostic containing the code needed to migrate to `@binaryAPI` or `@binaryAPIAccessor`.
109
+
- otherwise, an accessor is automatically generated and used.
140
110
141
-
Example 3:
111
+
Example:
142
112
~~~scala
113
+
importscala.annotation.binaryAPI
143
114
classC {
144
-
@binaryAPIAccessorprivatedefa:Int= ...
145
-
privatedefb:Int= ...
115
+
@binaryAPIprivate[C]defa:Int= ...
116
+
private[C]defb:Int= ...
146
117
@binaryAPI protecteddefc:Int= ...
147
118
protecteddefd:Int= ...
148
119
inlinedeffoo:Int= a + b + c + d
@@ -151,26 +122,100 @@ class C {
151
122
before inlining the compiler will generate the accessors for inlined definitions
152
123
~~~scala
153
124
classC {
154
-
@binaryAPIAccessorprivatedefa:Int= ...
155
-
privatedefb:Int= ...
125
+
@binaryAPIprivate[C]defa:Int= ...
126
+
private[C]defb:Int= ...
156
127
@binaryAPI protecteddefc:Int= ...
157
128
protecteddefd:Int= ...
158
-
finaldefC$$inline$a:Int= ... // generated by `@binaryAPIAccessor`
159
-
finaldefC$$inline$b:Int= ... // warn: `b` should be annotated with `@binaryAPIAccessor` + migration code
160
-
finaldefC$$inline$d:Int= ... // warn: `d` should be annotated with `@binaryAPI` + migration code
161
-
inlinedeffoo:Int=C$$inline$a +C$$inline$b + c +C$$inline$d
129
+
finaldefC$$inline$b:Int= ...
130
+
finaldefC$$inline$d:Int= ...
131
+
inlinedeffoo:Int= a +C$$inline$b + c +C$$inline$d
162
132
}
163
133
~~~
164
134
135
+
##### `-WunstableInlineAccessors`
136
+
137
+
In addition we introduce the `-WunstableInlineAccessors` flag to allow libraries to detect when the compiler generates unstable accessors.
138
+
The previous code would show a linter warning that looks like this:
| Unstable inline accessor C$$inline$d was generated in class C.
151
+
|
152
+
| longer explanation available when compiling with `-explain`
153
+
~~~
154
+
155
+
When an accessor is detected we can tell the user how to fix the issue. For example we could use the `-explain` flag to add the following details to the message.
@@ -179,63 +224,35 @@ final class binaryAPIAccessor extends scala.annotation.StaticAnnotation
179
224
* TASTy will contain references to non-public definitions that are out of scope but `@binaryAPI`. TASTy already allows those references.
180
225
* The annotated definitions will be public in the generated bytecode. Definitions should be made public as early as possible in the compiler phases, as this can remove the need to create other accessors. It should be done after we check the accessibility of references.
181
226
182
-
183
-
#### `@binaryAPIAccessor` annotation
184
-
185
-
This annotation is only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`.
186
-
187
-
A public accessor will be generated for the annotated definition. This accessor will be named `<fullClassName>$$inline$<definitionName>`. If the public accessor is in an object, the accessor will be named `inline$<definitionName>`. These names where chosen to minimize the complexity of migrating from automatically generates accessors in inline methods to `@binaryAPIAccessor` (see [Gist](https://gist.github.com/nicolasstucki/003f7293941836b08a0d53dbcb913e3c)).
188
-
189
227
#### Inline
190
228
191
229
* Inlining will not require the generation of an inline accessor for binary APIs.
192
-
* Inlining will not require the generation of a new inline accessor, it will use the binary API accessors.
193
-
* The user will be warned if a new inline accessor is automatically generated.
194
-
The message will suggest `@binaryAPI` or `@binaryAPIAccessor` and how to fix potential incompatibilities.
195
-
In a future version, these will become an error.
230
+
* The user will be warned if a new inline accessor is automatically generated under `-WunstableInlineAccessors`.
231
+
The message will suggest `@binaryAPI` and how to fix potential incompatibilities.
196
232
197
233
### Compatibility
198
234
199
-
The introduction of the `@binaryAPI` and `@binaryAPIAccessor` do not introduce any binary incompatibility.
200
-
201
-
Using references to `@binaryAPI` and `@binaryAPIAccessor` in inline code can cause binary incompatibilities. These incompatibilities are equivalent to the ones that can occur due to the unsoundness we want to fix. When migrating to binary APIs, the compiler will show the implementation of accessors that the users need to add to keep binary compatibility with pre-binaryAPI code.
235
+
The introduction of the `@binaryAPI` do not introduce any binary incompatibility.
202
236
203
-
A definition can be both `@binaryAPI`and `@binaryAPIAccessor`. This would be used to indicate that the definition used to be private, but now we want to publish it as public. The definition would become public, and the accessor would be generated for binary compatibility.
237
+
Using references to `@binaryAPI`in inline code can cause binary incompatibilities. These incompatibilities are equivalent to the ones that can occur due to the unsoundness we want to fix. When migrating to binary APIs, the compiler will show the implementation of accessors that the users need to add to keep binary compatibility with pre-binaryAPI code.
204
238
205
239
### Other concerns
206
240
207
241
* Tools that analyze inlined TASTy code might need to know about `@binaryAPI`. For example [MiMa](https://github.com/lightbend/mima/) and [TASTy MiMa](https://github.com/scalacenter/tasty-mima).
208
242
209
-
### Open questions
210
-
211
-
#### Question 1
212
-
Should `@binaryAPIAccessor` accessors be named `<fullClassName>$$<definitionName>`? This encoding would match the names of `trait` accessor generated for private definition. We could use a single accessor instead of two. This would introduce an extra binary incompatibility with pre-binaryAPI code.
// Should this use the accessor of generated for `A.protectedDef`? Or should we warn that `protectedDef` should be a `@binaryAPI`
222
-
protectedDef
223
-
```
224
-
225
243
## Alternatives
226
244
227
-
Having alternatives is not a strict requirement for a proposal, but having at least one with carefully exposed pros and cons gives much more weight to the proposal as a whole.
245
+
### Add a `@binaryAPIAccessor`
246
+
This annotation would generate an stable accessor. This annotation could be used on `private` definition. It would also mitigate [migration costs](https://gist.github.com/nicolasstucki/003f7293941836b08a0d53dbcb913e3c) for library authors that have published unstable accessors.
228
247
229
-
### Only add `@binaryAPI`
230
-
This would simplify the system and the user interaction with this feature. The drawback is that we could not access `private[this]` definitions in inline code. Users would need to use `private[C]` instead, which could cause name clashes.
This would simplify the system and the user interaction with this feature. The drawback is that we would add code size and runtime overhead to all uses of this feature. It would not solve the [Removing deprecated APIs](#removing-deprecated-apis) motivation.
234
250
235
251
## Related work
236
252
237
-
* Proof of concept: [https://github.com/lampepfl/dotty/pull/16992](https://github.com/lampepfl/dotty/pull/16992)
0 commit comments