From 3b7acde2e6713c838d5ecbba4172e36750d12602 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 27 Feb 2023 12:08:16 +0100 Subject: [PATCH 01/17] Add Binary APIs SIP --- content/binary-api.md | 211 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 content/binary-api.md diff --git a/content/binary-api.md b/content/binary-api.md new file mode 100644 index 00000000..8c755a2a --- /dev/null +++ b/content/binary-api.md @@ -0,0 +1,211 @@ +--- +layout: sip +permalink: /sips/:title.html +stage: pre-sip +status: submitted +title: SIP-52 - Binary APIs +--- + +**By: Author Nicolas Stucki** + +## History + +| Date | Version | +|---------------|--------------------| +| Feb 27 2022 | Initial Draft | + +## Summary + +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. + + +## Motivation + +### Provide a sound way to refer to private members in inline definitions + +Currently, the compiler automatically generates accessors for references to private members in inline definitions. This scheme interacts poorly with binary compatibility. It causes the following three unsoundness in the system: +* Changing any definition from private to public is a binary incompatible change +* Changing the implementation of an inline definition can be a binary incompatible change +* Removing final from a class is a binary incompatible change + +You can find more details in https://github.com/lampepfl/dotty/issues/16983 + +### Avoid duplication of inline accessors + +Ideally, private definitions should have a maximum of one inline accessor, which is not the case now. +When an inline method accesses a private/protected definition that is defined outside of its class, we generate an inline in the class of the inline method. This implies that accessors might be duplicated if a private/protected definition is accessed from different classes. + +### Removing deprecated APIs + +There is no precise mechanism to remove a deprecated method from a library without causing binary incompatibilities. We should have a straightforward way to indicate that a method is no longer publicly available but still available in the generated code for binary compatibility. + +```diff +- @deprecated(...) def myOldAPI: T = ... ++ private[C] def myOldAPI: T = ... +``` + + +## Proposed solution + +### High-level overview + +This proposal introduces 2 the `@binaryAPI` and `@binaryAPIAccessor` annotations, and changes adds a migration path to inline methods. + +#### `@binaryAPI` annotation + +A binary API is a definition that is annotated with `@binaryAPI` or overrides a definition annotated with `@binaryAPI`. +This annotation can be placed on `def`, `val`, `lazy val`, `var`, `object`, and `given` definitions. +A binary API will be publicly available in the bytecode. + +This annotation cannot be used on `private`/`private[this]` definitions. + +Removing this annotation from a non-public definition is a binary incompatible change. + +Example: + +~~~ scala +class C { + @binaryAPI private[C] def packagePrivateAPI: Int = ... + @binaryAPI protected def protectedAPI: Int = ... + @binaryAPI def publicAPI: Int = ... // warn: `@binaryAPI` has no effect on public definitions +} +~~~ +will generate the following bytecode signatures +~~~ java +public class C { + public C(); + public int packagePrivateAPI(); + public int protectedAPI(); + public int publicAPI(); +} +~~~ + +#### `@binaryAPIAccessor` annotation + +A binary API with accessor is a definition that is annotated with `@binaryAPIAccessor`. +This annotation can be placed on `def`, `val`, `lazy val`, `var`, `object`, and `given` definitions. +The annotated definition will get a public accessor. + +This can be used to access `private`/`private[this]` definitions within inline definitions. + +Example: +~~~ scala +class C { + @binaryAPIAccessor private def privateAPI: Int = ... + @binaryAPIAccessor def publicAPI: Int = ... +} +~~~ +will generate the following bytecode signatures +~~~ java +public class C { + public C(); + private int privateAPI(); + public int publicAPI(); + public final int C$$inline$privateAPI(); + public final int C$$inline$publicAPI(); +} +~~~ + +Note that the change from `private[this]` to package private, protected or public is a binary compatible change. +Removing this annotation is a binary incompatible change. + +#### Binary API and inlining + +If there is a reference to a binary API in an inline method we can use the definition without needing an inline accessor. + +Example 3: +~~~ scala +class C { + @binaryAPI protected def a: Int = ... + protected def b: Int = ... + inline def foo: Int = a + b +} +~~~ +before inlining the compiler will generate the accessors for inlined definitions +~~~ scala +class C { + @binaryAPI protected def a: Int = ... + protected def b: Int = ... + final def C$inline$b: Int = ... + inline def foo: Int = a + C$inline$b +} +~~~ + +Note that if the inlined member is `a` would be private, we would generate the accessor `C$inline$a`, which happens to be binary compatible with the automatically generated one. +This is only a tiny mitigation of binary compatibility issues compared with all the different ways accessors can be generated. + +### Specification + +We must add `binaryAPI` and `binaryAPIAccessor` to the standard library. + +```scala +package scala.annotation + +final class binaryAPI extends scala.annotation.StaticAnnotation +final class binaryAPIAccessor extends scala.annotation.StaticAnnotation +``` + +#### `@binaryAPI` annotation + +* Only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. +* TASTy will contain references to non-public definitions that are out of scope but `@binaryAPI`. TASTy already allows those references. +* Annotated definition 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. + + +#### `@binaryAPIAccessor` annotation + +* Only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. +* An public accessor will be generated for the annotated definition. This accessor will be named `$$inline$`. + +#### Inline + +* Inlining will not require the generation of an inline accessor for binary APIs. +* Inlining will not require the generation of a new inline accessor, it will use the binary API accessors. +* The user will be warned if a new inline accessor is automatically generated. + The message will suggest `@binaryAPI` or `@binaryAPIAccessor` and how to fix potential incompatibilities. + In a future version, these will become an error. + +### Compatibility + +The introduction of the `@binaryAPI` and `@binaryAPIAccessor` do not introduce any binary incompatibility. + +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. + +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. + +### Other concerns + +* Tools that analyze inlined TASTy code might need to know about `@binaryAPI`. For example TASTy MiMa. + +### Open questions + +#### Question 1 +Should `@binaryAPIAccessor` accessors be named `$$`? 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. + +#### Question 2 +```scala +class A: + @binaryAPIAccessor protected def protectedDef: Int = ... +class B extends A: + override protected def protectedDef: Int = ... + inline def inlinedDef: Int = + // Should this use the accessor of generated for `A.protectedDef`? Or should we warn that `protectedDef` should be a `@binaryAPI` + protectedDef +``` + +## Alternatives + +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. --> + +### Only add `@binaryAPI` +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. + +### Only add `@binaryAPIAccessor` +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. + +## Related work + +* Proof of concept: https://github.com/lampepfl/dotty/pull/16992 +* Initial discussions: https://github.com/lampepfl/dotty/issues/16983 + + From 98068259c769940ee87b439aeefa39b3775dcde4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 2 Mar 2023 08:36:51 +0100 Subject: [PATCH 02/17] Add MiMa classic to the list of other concerns. --- content/binary-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/binary-api.md b/content/binary-api.md index 8c755a2a..9e33b415 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -175,7 +175,7 @@ A definition can be both `@binaryAPI` and `@binaryAPIAccessor`. This would be us ### Other concerns -* Tools that analyze inlined TASTy code might need to know about `@binaryAPI`. For example TASTy MiMa. +* 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). ### Open questions From de9c9df157a48d2c6f548e736173bf268a9cc7bc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 2 Mar 2023 08:44:03 +0100 Subject: [PATCH 03/17] Add reference to discussion on `private[pkg]` --- content/binary-api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/binary-api.md b/content/binary-api.md index 9e33b415..298ea073 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -44,6 +44,7 @@ There is no precise mechanism to remove a deprecated method from a library witho + private[C] def myOldAPI: T = ... ``` +Related to discussion in https://github.com/lightbend/mima/discussions/724 ## Proposed solution From 0861c51698e89369c4c88f43a06afd5591bfb6d3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 14 Mar 2023 14:37:40 +0100 Subject: [PATCH 04/17] Add mention to ACC_PUBLIC and ACC_SYNTHETIC --- content/binary-api.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/binary-api.md b/content/binary-api.md index 298ea073..5d1ca7c4 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -81,6 +81,9 @@ public class C { } ~~~ +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. + + #### `@binaryAPIAccessor` annotation A binary API with accessor is a definition that is annotated with `@binaryAPIAccessor`. @@ -110,6 +113,8 @@ public class C { Note that the change from `private[this]` to package private, protected or public is a binary compatible change. Removing this annotation is a binary incompatible change. +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. + #### Binary API and inlining If there is a reference to a binary API in an inline method we can use the definition without needing an inline accessor. From 19165b1b113f01368eaac6a236d8c95773423e3a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 Mar 2023 09:57:10 +0100 Subject: [PATCH 05/17] Allow to use binaryAPI on private constructors. --- content/binary-api.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/content/binary-api.md b/content/binary-api.md index 5d1ca7c4..de46c5f7 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -46,6 +46,10 @@ There is no precise mechanism to remove a deprecated method from a library witho Related to discussion in https://github.com/lightbend/mima/discussions/724 +### No way to inline reference to private constructors + +It is currently impossible to refer to private constructors in inline methods. If users want to access one of those, they must write an accessor explicitly. This extra indirection is undesirable. + ## Proposed solution ### High-level overview @@ -58,7 +62,7 @@ A binary API is a definition that is annotated with `@binaryAPI` or overrides a This annotation can be placed on `def`, `val`, `lazy val`, `var`, `object`, and `given` definitions. A binary API will be publicly available in the bytecode. -This annotation cannot be used on `private`/`private[this]` definitions. +This annotation cannot be used on `private`/`private[this]` definitions. With the exception of class constructors. Removing this annotation from a non-public definition is a binary incompatible change. From 2857ccd0dfd36595802c4227c02232945c417468 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 Mar 2023 10:27:00 +0100 Subject: [PATCH 06/17] Update binary API and inlining example --- content/binary-api.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/content/binary-api.md b/content/binary-api.md index de46c5f7..9d7793c9 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -121,29 +121,35 @@ In the bytecode, `@binaryAPIAccessor` generated accessors will have the [ACC_PUB #### Binary API and inlining -If there is a reference to a binary API in an inline method we can use the definition without needing an inline accessor. +A non-public reference in an inline method is handled as follows: + - if the reference is a `@binaryAPI` the reference is used; + - if the reference is a `@binaryAPIAccessor` the accessor is used; + - 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`. Example 3: ~~~ scala class C { - @binaryAPI protected def a: Int = ... - protected def b: Int = ... - inline def foo: Int = a + b + @binaryAPIAccessor private def a: Int = ... + private def b: Int = ... + @binaryAPI protected def c: Int = ... + protected def d: Int = ... + inline def foo: Int = a + b + c + d } ~~~ before inlining the compiler will generate the accessors for inlined definitions ~~~ scala class C { - @binaryAPI protected def a: Int = ... - protected def b: Int = ... - final def C$inline$b: Int = ... - inline def foo: Int = a + C$inline$b + @binaryAPIAccessor private def a: Int = ... + private def b: Int = ... + @binaryAPI protected def c: Int = ... + protected def d: Int = ... + final def C$inline$a: Int = ... // generated by `@binaryAPIAccessor` + final def C$inline$b: Int = ... // warn: `b` should be annotated with `@binaryAPIAccessor` + migration code + final def C$inline$d: Int = ... // warn: `d` should be annotated with `@binaryAPI` + migration code + inline def foo: Int = C$inline$a + C$inline$b + c + C$inline$d } ~~~ -Note that if the inlined member is `a` would be private, we would generate the accessor `C$inline$a`, which happens to be binary compatible with the automatically generated one. -This is only a tiny mitigation of binary compatibility issues compared with all the different ways accessors can be generated. - ### Specification We must add `binaryAPI` and `binaryAPIAccessor` to the standard library. From eb57925db13a81e0b54af99e0acfcef1ad2ef9d1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 11 May 2023 09:16:49 +0200 Subject: [PATCH 07/17] Apply suggestions from code review Co-authored-by: Julien Richard-Foy --- content/binary-api.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/content/binary-api.md b/content/binary-api.md index 9d7793c9..ddcd6d03 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -28,7 +28,7 @@ Currently, the compiler automatically generates accessors for references to priv * Changing the implementation of an inline definition can be a binary incompatible change * Removing final from a class is a binary incompatible change -You can find more details in https://github.com/lampepfl/dotty/issues/16983 +You can find more details in [https://github.com/lampepfl/dotty/issues/16983](https://github.com/lampepfl/dotty/issues/16983) ### Avoid duplication of inline accessors @@ -44,7 +44,7 @@ There is no precise mechanism to remove a deprecated method from a library witho + private[C] def myOldAPI: T = ... ``` -Related to discussion in https://github.com/lightbend/mima/discussions/724 +Related to discussion in [https://github.com/lightbend/mima/discussions/724](https://github.com/lightbend/mima/discussions/724). ### No way to inline reference to private constructors @@ -54,7 +54,7 @@ It is currently impossible to refer to private constructors in inline methods. I ### High-level overview -This proposal introduces 2 the `@binaryAPI` and `@binaryAPIAccessor` annotations, and changes adds a migration path to inline methods. +This proposal introduces 2 the `@binaryAPI` and `@binaryAPIAccessor` annotations, and adds a migration path to inline methods. #### `@binaryAPI` annotation @@ -165,13 +165,13 @@ final class binaryAPIAccessor extends scala.annotation.StaticAnnotation * Only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. * TASTy will contain references to non-public definitions that are out of scope but `@binaryAPI`. TASTy already allows those references. -* Annotated definition 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. +* 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. #### `@binaryAPIAccessor` annotation * Only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. -* An public accessor will be generated for the annotated definition. This accessor will be named `$$inline$`. +* A public accessor will be generated for the annotated definition. This accessor will be named `$$inline$`. #### Inline @@ -221,7 +221,7 @@ This would simplify the system and the user interaction with this feature. The d ## Related work -* Proof of concept: https://github.com/lampepfl/dotty/pull/16992 -* Initial discussions: https://github.com/lampepfl/dotty/issues/16983 +* Proof of concept: [https://github.com/lampepfl/dotty/pull/16992](https://github.com/lampepfl/dotty/pull/16992) +* Initial discussions: [https://github.com/lampepfl/dotty/issues/16983](https://github.com/lampepfl/dotty/issues/16983) From 1d22f521ab9a2559217733b242a0c83124824d58 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 11 May 2023 11:47:48 +0200 Subject: [PATCH 08/17] Add private constructors example --- content/binary-api.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/content/binary-api.md b/content/binary-api.md index ddcd6d03..4adae44b 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -48,7 +48,19 @@ Related to discussion in [https://github.com/lightbend/mima/discussions/724](htt ### No way to inline reference to private constructors -It is currently impossible to refer to private constructors in inline methods. If users want to access one of those, they must write an accessor explicitly. This extra indirection is undesirable. +It is currently impossible to refer to private constructors in inline methods. +```scala +class C private() +object C: + inline def newC: C = new C() // Implementation restriction: cannot use private constructors in inline methods +``` +If users want to access one of those, they must write an accessor explicitly. This extra indirection is undesirable. +```scala +class C private() +object C: + private def newCInternal: C = new C() + inline def newC: C = newCInternal +``` ## Proposed solution From 23c29c7aa91a34b325f7acc93a1c67151e7e29ba Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 May 2023 08:57:32 +0200 Subject: [PATCH 09/17] Special case accessor names in objects This is to make migration from auto generated accessors to `@binaryAPIAccessors` easier. --- content/binary-api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/binary-api.md b/content/binary-api.md index 4adae44b..9abbf38e 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -184,6 +184,7 @@ final class binaryAPIAccessor extends scala.annotation.StaticAnnotation * Only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. * A public accessor will be generated for the annotated definition. This accessor will be named `$$inline$`. +* If the public accessor is in an object, the accessor will be named `inline$`. #### Inline @@ -223,7 +224,7 @@ class B extends A: ## Alternatives -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. --> +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. ### Only add `@binaryAPI` 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. From 2af1384dfdbec46f44d3a666586403cf73633737 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 May 2023 09:00:43 +0200 Subject: [PATCH 10/17] Fix example --- content/binary-api.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/binary-api.md b/content/binary-api.md index 9abbf38e..e5bda2ff 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -155,10 +155,10 @@ class C { private def b: Int = ... @binaryAPI protected def c: Int = ... protected def d: Int = ... - final def C$inline$a: Int = ... // generated by `@binaryAPIAccessor` - final def C$inline$b: Int = ... // warn: `b` should be annotated with `@binaryAPIAccessor` + migration code - final def C$inline$d: Int = ... // warn: `d` should be annotated with `@binaryAPI` + migration code - inline def foo: Int = C$inline$a + C$inline$b + c + C$inline$d + final def C$$inline$a: Int = ... // generated by `@binaryAPIAccessor` + final def C$$inline$b: Int = ... // warn: `b` should be annotated with `@binaryAPIAccessor` + migration code + final def C$$inline$d: Int = ... // warn: `d` should be annotated with `@binaryAPI` + migration code + inline def foo: Int = C$$inline$a + C$$inline$b + c + C$$inline$d } ~~~ From c5b585cc981b778317213a50a19a04e52392b1cf Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 May 2023 09:07:39 +0200 Subject: [PATCH 11/17] Add link to migration analysis --- content/binary-api.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/content/binary-api.md b/content/binary-api.md index e5bda2ff..7c052db9 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -182,9 +182,9 @@ final class binaryAPIAccessor extends scala.annotation.StaticAnnotation #### `@binaryAPIAccessor` annotation -* Only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. -* A public accessor will be generated for the annotated definition. This accessor will be named `$$inline$`. -* If the public accessor is in an object, the accessor will be named `inline$`. +This annotation is only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. + +A public accessor will be generated for the annotated definition. This accessor will be named `$$inline$`. If the public accessor is in an object, the accessor will be named `inline$`. 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)). #### Inline @@ -236,5 +236,6 @@ This would simplify the system and the user interaction with this feature. The d * Proof of concept: [https://github.com/lampepfl/dotty/pull/16992](https://github.com/lampepfl/dotty/pull/16992) * Initial discussions: [https://github.com/lampepfl/dotty/issues/16983](https://github.com/lampepfl/dotty/issues/16983) +* Community migration analysis: [Gist](https://gist.github.com/nicolasstucki/003f7293941836b08a0d53dbcb913e3c) From 8ea645a49c0069735db07b7d2781805b9ba0cef3 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 16 May 2023 10:00:08 +0200 Subject: [PATCH 12/17] Make file mergeable --- content/binary-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/binary-api.md b/content/binary-api.md index 7c052db9..f3bbd87f 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -1,8 +1,8 @@ --- layout: sip permalink: /sips/:title.html -stage: pre-sip -status: submitted +stage: implementation +status: waiting-for-implementation title: SIP-52 - Binary APIs --- From dd862f85e1e1684c3692e2ed66fb70712aee7576 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 16 Aug 2023 11:15:48 +0200 Subject: [PATCH 13/17] Update to a single annotation with linter warning --- content/binary-api.md | 183 +++++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 83 deletions(-) diff --git a/content/binary-api.md b/content/binary-api.md index f3bbd87f..8e2e975b 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -13,10 +13,12 @@ title: SIP-52 - Binary APIs | Date | Version | |---------------|--------------------| | Feb 27 2022 | Initial Draft | +| Aug 16 2022 | Single Annotation | ## Summary -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. +The purpose of binary APIs is to have publicly accessible definitions in generated bytecode for definitions that are package private or protected. +This proposal introduces the `@binaryAPI` annotation on term definitions and the `-WunstableInlineAccessors` linting flag. ## Motivation @@ -66,7 +68,7 @@ object C: ### High-level overview -This proposal introduces 2 the `@binaryAPI` and `@binaryAPIAccessor` annotations, and adds a migration path to inline methods. +This proposal introduces the `@binaryAPI` annotation, and adds a migration path to inline methods in libraries (requiring binary compatibility). #### `@binaryAPI` annotation @@ -100,49 +102,18 @@ public class C { 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. -#### `@binaryAPIAccessor` annotation - -A binary API with accessor is a definition that is annotated with `@binaryAPIAccessor`. -This annotation can be placed on `def`, `val`, `lazy val`, `var`, `object`, and `given` definitions. -The annotated definition will get a public accessor. - -This can be used to access `private`/`private[this]` definitions within inline definitions. - -Example: -~~~ scala -class C { - @binaryAPIAccessor private def privateAPI: Int = ... - @binaryAPIAccessor def publicAPI: Int = ... -} -~~~ -will generate the following bytecode signatures -~~~ java -public class C { - public C(); - private int privateAPI(); - public int publicAPI(); - public final int C$$inline$privateAPI(); - public final int C$$inline$publicAPI(); -} -~~~ - -Note that the change from `private[this]` to package private, protected or public is a binary compatible change. -Removing this annotation is a binary incompatible change. - -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. - #### Binary API and inlining A non-public reference in an inline method is handled as follows: - if the reference is a `@binaryAPI` the reference is used; - - if the reference is a `@binaryAPIAccessor` the accessor is used; - - 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`. + - otherwise, an accessor is automatically generated and used. -Example 3: +Example: ~~~ scala +import scala.annotation.binaryAPI class C { - @binaryAPIAccessor private def a: Int = ... - private def b: Int = ... + @binaryAPI private[C] def a: Int = ... + private[C] def b: Int = ... @binaryAPI protected def c: Int = ... protected def d: Int = ... inline def foo: Int = a + b + c + d @@ -151,26 +122,100 @@ class C { before inlining the compiler will generate the accessors for inlined definitions ~~~ scala class C { - @binaryAPIAccessor private def a: Int = ... - private def b: Int = ... + @binaryAPI private[C] def a: Int = ... + private[C] def b: Int = ... @binaryAPI protected def c: Int = ... protected def d: Int = ... - final def C$$inline$a: Int = ... // generated by `@binaryAPIAccessor` - final def C$$inline$b: Int = ... // warn: `b` should be annotated with `@binaryAPIAccessor` + migration code - final def C$$inline$d: Int = ... // warn: `d` should be annotated with `@binaryAPI` + migration code - inline def foo: Int = C$$inline$a + C$$inline$b + c + C$$inline$d + final def C$$inline$b: Int = ... + final def C$$inline$d: Int = ... + inline def foo: Int = a + C$$inline$b + c + C$$inline$d } ~~~ +##### `-WunstableInlineAccessors` + +In addition we introduce the `-WunstableInlineAccessors` flag to allow libraries to detect when the compiler generates unstable accessors. +The previous code would show a linter warning that looks like this: + +~~~ +-- [E...] Compatibility Warning: C.scala ----------------------------- + | inline def foo: Int = a + b + c + d + | ^ + | Unstable inline accessor C$$inline$b was generated in class C. + | + | longer explanation available when compiling with `-explain` +-- [E...] Compatibility Warning: C.scala ----------------------------- + | inline def foo: Int = a + b + c + d + | ^ + | Unstable inline accessor C$$inline$d was generated in class C. + | + | longer explanation available when compiling with `-explain` +~~~ + +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. + +
+With `-WunstableInlineAccessors -explain` + +~~~ +-- [E...] Compatibility Warning: C.scala ----------------------------- + | inline def foo: Int = a + b + c + d + | ^ + | Unstable inline accessor C$$inline$b was generated in class C. + |----------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Access to non-public method b causes the automatic generation of an accessor. + | This accessor is not stable, its name may change or it may disappear + | if not needed in a future version. + | + | To make sure that the inlined code is binary compatible you must make sure that + | method b is public in the binary API. + | * Option 1: Annotate method b with @binaryAPI + | * Option 2: Make method b public + | + | This change may break binary compatibility if a previous version of this + | library was compiled with generated accessors. Binary compatibility should + | be checked using MiMa. If binary compatibility is broken, you should add the + | old accessor explicitly in the source code. The following code should be + | added to class C: + | @binaryAPI private[C] def C$$inline$b: Int = this.b + ----------------------------------------------------------------------------- +-- [E...] Compatibility Warning: C.scala ----------------------------- + | inline def foo: Int = a + b + c + d + | ^ + | Unstable inline accessor C$$inline$d was generated in class C. + |----------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Access to non-public method d causes the automatic generation of an accessor. + | This accessor is not stable, its name may change or it may disappear + | if not needed in a future version. + | + | To make sure that the inlined code is binary compatible you must make sure that + | method d is public in the binary API. + | * Option 1: Annotate method d with @binaryAPI + | * Option 2: Make method d public + | + | This change may break binary compatibility if a previous version of this + | library was compiled with generated accessors. Binary compatibility should + | be checked using MiMa. If binary compatibility is broken, you should add the + | old accessor explicitly in the source code. The following code should be + | added to class C: + | @binaryAPI private[C] def C$$inline$d: Int = this.d + ----------------------------------------------------------------------------- +~~~ + +
+ ### Specification -We must add `binaryAPI` and `binaryAPIAccessor` to the standard library. +We must add `binaryAPI` to the standard library. ```scala package scala.annotation final class binaryAPI extends scala.annotation.StaticAnnotation -final class binaryAPIAccessor extends scala.annotation.StaticAnnotation ``` #### `@binaryAPI` annotation @@ -179,63 +224,35 @@ final class binaryAPIAccessor extends scala.annotation.StaticAnnotation * TASTy will contain references to non-public definitions that are out of scope but `@binaryAPI`. TASTy already allows those references. * 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. - -#### `@binaryAPIAccessor` annotation - -This annotation is only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. - -A public accessor will be generated for the annotated definition. This accessor will be named `$$inline$`. If the public accessor is in an object, the accessor will be named `inline$`. 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)). - #### Inline * Inlining will not require the generation of an inline accessor for binary APIs. -* Inlining will not require the generation of a new inline accessor, it will use the binary API accessors. -* The user will be warned if a new inline accessor is automatically generated. - The message will suggest `@binaryAPI` or `@binaryAPIAccessor` and how to fix potential incompatibilities. - In a future version, these will become an error. +* The user will be warned if a new inline accessor is automatically generated under `-WunstableInlineAccessors`. + The message will suggest `@binaryAPI` and how to fix potential incompatibilities. ### Compatibility -The introduction of the `@binaryAPI` and `@binaryAPIAccessor` do not introduce any binary incompatibility. - -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. +The introduction of the `@binaryAPI` do not introduce any binary incompatibility. -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. +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. ### Other concerns * 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). -### Open questions - -#### Question 1 -Should `@binaryAPIAccessor` accessors be named `$$`? 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. - -#### Question 2 -```scala -class A: - @binaryAPIAccessor protected def protectedDef: Int = ... -class B extends A: - override protected def protectedDef: Int = ... - inline def inlinedDef: Int = - // Should this use the accessor of generated for `A.protectedDef`? Or should we warn that `protectedDef` should be a `@binaryAPI` - protectedDef -``` - ## Alternatives -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. +### Add a `@binaryAPIAccessor` +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. -### Only add `@binaryAPI` -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. +* Implementation https://github.com/lampepfl/dotty/pull/16992 -### Only add `@binaryAPIAccessor` -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. ## Related work -* Proof of concept: [https://github.com/lampepfl/dotty/pull/16992](https://github.com/lampepfl/dotty/pull/16992) * Initial discussions: [https://github.com/lampepfl/dotty/issues/16983](https://github.com/lampepfl/dotty/issues/16983) +* Initial proof of concept (outdated): [https://github.com/lampepfl/dotty/pull/16992](https://github.com/lampepfl/dotty/pull/16992) +* Single annotation proof of concept: [https://github.com/lampepfl/dotty/pull/18402](https://github.com/lampepfl/dotty/pull/18402) * Community migration analysis: [Gist](https://gist.github.com/nicolasstucki/003f7293941836b08a0d53dbcb913e3c) From 46bd277e7720c7bf2ea318da33161dc74080c2c4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 24 Aug 2023 09:21:40 +0200 Subject: [PATCH 14/17] Change name of annotation to `@publicInBinary` --- content/binary-api.md | 63 ++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/content/binary-api.md b/content/binary-api.md index 8e2e975b..88cd5236 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -10,15 +10,16 @@ title: SIP-52 - Binary APIs ## History -| Date | Version | -|---------------|--------------------| -| Feb 27 2022 | Initial Draft | -| Aug 16 2022 | Single Annotation | +| Date | Version | +|---------------|------------------------| +| Feb 27 2022 | Initial Draft | +| Aug 16 2022 | Single Annotation | +| Aug 24 2022 | Change Annotation Name | ## Summary The purpose of binary APIs is to have publicly accessible definitions in generated bytecode for definitions that are package private or protected. -This proposal introduces the `@binaryAPI` annotation on term definitions and the `-WunstableInlineAccessors` linting flag. +This proposal introduces the `@publicInBinary` annotation on term definitions and the `-WunstableInlineAccessors` linting flag. ## Motivation @@ -68,11 +69,11 @@ object C: ### High-level overview -This proposal introduces the `@binaryAPI` annotation, and adds a migration path to inline methods in libraries (requiring binary compatibility). +This proposal introduces the `@publicInBinary` annotation, and adds a migration path to inline methods in libraries (requiring binary compatibility). -#### `@binaryAPI` annotation +#### `@publicInBinary` annotation -A binary API is a definition that is annotated with `@binaryAPI` or overrides a definition annotated with `@binaryAPI`. +A binary API is a definition that is annotated with `@publicInBinary` or overrides a definition annotated with `@publicInBinary`. This annotation can be placed on `def`, `val`, `lazy val`, `var`, `object`, and `given` definitions. A binary API will be publicly available in the bytecode. @@ -84,9 +85,9 @@ Example: ~~~ scala class C { - @binaryAPI private[C] def packagePrivateAPI: Int = ... - @binaryAPI protected def protectedAPI: Int = ... - @binaryAPI def publicAPI: Int = ... // warn: `@binaryAPI` has no effect on public definitions + @publicInBinary private[C] def packagePrivateAPI: Int = ... + @publicInBinary protected def protectedAPI: Int = ... + @publicInBinary def publicAPI: Int = ... // warn: `@publicInBinary` has no effect on public definitions } ~~~ will generate the following bytecode signatures @@ -99,22 +100,22 @@ public class C { } ~~~ -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. +In the bytecode, `@publicInBinary` 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. #### Binary API and inlining A non-public reference in an inline method is handled as follows: - - if the reference is a `@binaryAPI` the reference is used; + - if the reference is a `@publicInBinary` the reference is used; - otherwise, an accessor is automatically generated and used. Example: ~~~ scala -import scala.annotation.binaryAPI +import scala.annotation.publicInBinary class C { - @binaryAPI private[C] def a: Int = ... + @publicInBinary private[C] def a: Int = ... private[C] def b: Int = ... - @binaryAPI protected def c: Int = ... + @publicInBinary protected def c: Int = ... protected def d: Int = ... inline def foo: Int = a + b + c + d } @@ -122,9 +123,9 @@ class C { before inlining the compiler will generate the accessors for inlined definitions ~~~ scala class C { - @binaryAPI private[C] def a: Int = ... + @publicInBinary private[C] def a: Int = ... private[C] def b: Int = ... - @binaryAPI protected def c: Int = ... + @publicInBinary protected def c: Int = ... protected def d: Int = ... final def C$$inline$b: Int = ... final def C$$inline$d: Int = ... @@ -171,7 +172,7 @@ When an accessor is detected we can tell the user how to fix the issue. For exam | | To make sure that the inlined code is binary compatible you must make sure that | method b is public in the binary API. - | * Option 1: Annotate method b with @binaryAPI + | * Option 1: Annotate method b with @publicInBinary | * Option 2: Make method b public | | This change may break binary compatibility if a previous version of this @@ -179,7 +180,7 @@ When an accessor is detected we can tell the user how to fix the issue. For exam | be checked using MiMa. If binary compatibility is broken, you should add the | old accessor explicitly in the source code. The following code should be | added to class C: - | @binaryAPI private[C] def C$$inline$b: Int = this.b + | @publicInBinary private[C] def C$$inline$b: Int = this.b ----------------------------------------------------------------------------- -- [E...] Compatibility Warning: C.scala ----------------------------- | inline def foo: Int = a + b + c + d @@ -194,7 +195,7 @@ When an accessor is detected we can tell the user how to fix the issue. For exam | | To make sure that the inlined code is binary compatible you must make sure that | method d is public in the binary API. - | * Option 1: Annotate method d with @binaryAPI + | * Option 1: Annotate method d with @publicInBinary | * Option 2: Make method d public | | This change may break binary compatibility if a previous version of this @@ -202,7 +203,7 @@ When an accessor is detected we can tell the user how to fix the issue. For exam | be checked using MiMa. If binary compatibility is broken, you should add the | old accessor explicitly in the source code. The following code should be | added to class C: - | @binaryAPI private[C] def C$$inline$d: Int = this.d + | @publicInBinary private[C] def C$$inline$d: Int = this.d ----------------------------------------------------------------------------- ~~~ @@ -210,39 +211,39 @@ When an accessor is detected we can tell the user how to fix the issue. For exam ### Specification -We must add `binaryAPI` to the standard library. +We must add `publicInBinary` to the standard library. ```scala package scala.annotation -final class binaryAPI extends scala.annotation.StaticAnnotation +final class publicInBinary extends scala.annotation.StaticAnnotation ``` -#### `@binaryAPI` annotation +#### `@publicInBinary` annotation * Only valid on `def`, `val`, `lazy val`, `var`, `object`, and `given`. -* TASTy will contain references to non-public definitions that are out of scope but `@binaryAPI`. TASTy already allows those references. +* TASTy will contain references to non-public definitions that are out of scope but `@publicInBinary`. TASTy already allows those references. * 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. #### Inline * Inlining will not require the generation of an inline accessor for binary APIs. * The user will be warned if a new inline accessor is automatically generated under `-WunstableInlineAccessors`. - The message will suggest `@binaryAPI` and how to fix potential incompatibilities. + The message will suggest `@publicInBinary` and how to fix potential incompatibilities. ### Compatibility -The introduction of the `@binaryAPI` do not introduce any binary incompatibility. +The introduction of the `@publicInBinary` do not introduce any binary incompatibility. -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. +Using references to `@publicInBinary` 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-publicInBinary code. ### Other concerns -* 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). +* Tools that analyze inlined TASTy code might need to know about `@publicInBinary`. For example [MiMa](https://github.com/lightbend/mima/) and [TASTy MiMa](https://github.com/scalacenter/tasty-mima). ## Alternatives -### Add a `@binaryAPIAccessor` +### Add a `@binaryAccessor` 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. * Implementation https://github.com/lampepfl/dotty/pull/16992 From f1dbf18ffb30dc80b55f635340b3e8b4bd102c64 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 24 Aug 2023 09:42:31 +0200 Subject: [PATCH 15/17] Add comment on `private[C]` and public binary API compatibility --- content/binary-api.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/content/binary-api.md b/content/binary-api.md index 88cd5236..ebabc285 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -239,7 +239,7 @@ Using references to `@publicInBinary` in inline code can cause binary incompatib ### Other concerns -* Tools that analyze inlined TASTy code might need to know about `@publicInBinary`. For example [MiMa](https://github.com/lightbend/mima/) and [TASTy MiMa](https://github.com/scalacenter/tasty-mima). +* Tools that analyze inlined TASTy code will need to know about `@publicInBinary`. For example [MiMa](https://github.com/lightbend/mima/) and [TASTy MiMa](https://github.com/scalacenter/tasty-mima). ## Alternatives @@ -249,6 +249,20 @@ This annotation would generate an stable accessor. This annotation could be used * Implementation https://github.com/lampepfl/dotty/pull/16992 +### Make all `private[C]` part of the binary API + +Currently, we already make `private[C]` public in the binary API but do not guarantee but do not have the same guarantees regarding binary compatibility. +For example, the following change is binary compatible but would remove the existence of the `private[C]` definition in the bytecode. +```diff +class C: +- private[C] def f: T = ... +``` +We could change the rules to make all `private[C]` part of binary compatible to flag such a change as binary incompatible. This would imply that all these +methods can be accessed directly from inline methods without generating an accessor. + +The drawback of this approach is that that we would need to force users to keep their `private[C]` methods even if they never used inline methods. + + ## Related work * Initial discussions: [https://github.com/lampepfl/dotty/issues/16983](https://github.com/lampepfl/dotty/issues/16983) From 64f79d087d5b04238b63b2a03198f8683d96cf46 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 24 Aug 2023 09:43:30 +0200 Subject: [PATCH 16/17] Add reference to Kotlin's PublishedApi --- content/binary-api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/binary-api.md b/content/binary-api.md index ebabc285..4d5f4aa5 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -269,5 +269,8 @@ The drawback of this approach is that that we would need to force users to keep * Initial proof of concept (outdated): [https://github.com/lampepfl/dotty/pull/16992](https://github.com/lampepfl/dotty/pull/16992) * Single annotation proof of concept: [https://github.com/lampepfl/dotty/pull/18402](https://github.com/lampepfl/dotty/pull/18402) * Community migration analysis: [Gist](https://gist.github.com/nicolasstucki/003f7293941836b08a0d53dbcb913e3c) +* Kotlin: [PublishedApi](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-published-api/) plays the same role as `@publicInBinary` + but its interaction with (inline definitions)[https://kotlinlang.org/docs/inline-functions.html#restrictions-for-public-api-inline-functions] + is stricter as they do not support automatic accessor generation. From ca4f97d578b2b2fc561dede477544bcd04044679 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 30 Aug 2023 17:03:39 +0200 Subject: [PATCH 17/17] Fix typo --- content/binary-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/binary-api.md b/content/binary-api.md index 4d5f4aa5..5b7ccdd9 100644 --- a/content/binary-api.md +++ b/content/binary-api.md @@ -251,7 +251,7 @@ This annotation would generate an stable accessor. This annotation could be used ### Make all `private[C]` part of the binary API -Currently, we already make `private[C]` public in the binary API but do not guarantee but do not have the same guarantees regarding binary compatibility. +Currently, we already make `private[C]` public in the binary API but do not have the same guarantees regarding binary compatibility. For example, the following change is binary compatible but would remove the existence of the `private[C]` definition in the bytecode. ```diff class C: