Skip to content

Commit 8dd9496

Browse files
author
Anastasia Pikalova
committed
Update proposals/subclass-opt-in-required.md
1 parent eeaddfc commit 8dd9496

File tree

1 file changed

+76
-2
lines changed

1 file changed

+76
-2
lines changed

proposals/subclass-opt-in-required.md

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
* **Type**: Design proposal
44
* **Author**: Vsevolod Tolstopyatov
5-
* **Contributors**: Mikhail Glukhikh, Vsevolod Tolstopyatov, Roman Elizarov, Ilya Gorbunov
5+
* **Contributors**: Mikhail Glukhikh, Vsevolod Tolstopyatov, Roman Elizarov, Ilya Gorbunov, Anastasia Pikalova,
6+
Stanislav Ruban
7+
68
* **Status**: Implemented in Kotlin 1.8.0 as experimental
79
* **Discussion**: [KEEP-320](https://github.com/Kotlin/KEEP/issues/320)
810

@@ -101,12 +103,16 @@ Other alternatives are:
101103

102104
* `RequireInheritanceOptiIn`
103105
* `SubclassesRequireOptIn`
106+
* `SubclassRequiresOptIn`
107+
* `InheritanceRequiresOptIn`
104108
* Various attempts to leverage the notion of `sealed` and `open`:
105109
* `SemiOpen` and `SemiSealed`
106110
* `OptInToOpen` and `OptInToSubclass`
107111

108112
`SubclassOptInRequired` was chosen as the most appropriate and likely the most familiar for developers
109113
to grasp from at first glance.
114+
The name indicates that subclasses must opt in.
115+
`Required` highlights this obligation more effectively than `Requires`.
110116

111117
### SubclassOptInRequired marker contagiousness (lexical scopes)
112118

@@ -184,8 +190,76 @@ may be used within its body or signatures (`UnstableApi` types or overridden met
184190
between
185191
opting-in into extension and opting-in into overall uses.
186192

193+
### Design downsides
194+
Although one of the goals of this proposal is consistency with the existing `OptIn` API,
195+
`SubclassOptInRequired` doesn't support passing annotation arguments, unlike experimental annotations.
196+
For example:
197+
```kotlin
198+
@RequiresOptIn
199+
annotation class ExperimentalAPI(val message: String)
200+
201+
@ExperimentalAPI("Some message")
202+
class ExperimentalA
203+
204+
// Unable to set 'message'
205+
@SubclassOptInRequired(ExperimentalAPI::class)
206+
open class ExperimentalB
207+
```
208+
It's allowed to pass a custom message as an annotation argument in `ExperimentalAPI`,
209+
but this is not possible with `SubclassOptInRequired`.
210+
This design limitation is considered minor
211+
because no significant use cases or valid scenarios for annotation arguments in experimental annotations have been identified.
212+
213+
### Alternative approaches
214+
1. `@RequiresOptIn` injects a new `scope` parameter with the default value `ALL` to an experimental annotation.
215+
```kotlin
216+
@RequiresOptIn // injects 'scope' param
217+
annotation class Ann
218+
219+
@Ann(scope = Scope.Inheritance)
220+
open class Foo
221+
```
222+
The design was rejected due to concerns about preserving source compatibility with explicitly declared and injected parameters.
223+
224+
225+
2. Users can define a special annotation parameter named `scope`.
226+
```kotlin
227+
@RequiresOptIn
228+
annotation class Ann(val scope: Scope = Scope.All)
229+
230+
@Ann(scope = Scope.Inheritance)
231+
open class Foo
232+
```
233+
The design was rejected because it creates an implicit contract between the compiler logic and the parameter names,
234+
leading to potential fragility dependencies.
235+
236+
237+
3. Pass annotation instances as arguments to the `@SubclassOptInRequired` annotation.
238+
```kotlin
239+
@RequiresOptIn
240+
annotation class Ann(val message: String)
241+
242+
@SubclassOptInRequired(@Ann("message"))
243+
open class Foo
244+
```
245+
The design was rejected because the `Annotation` type, which is common to all annotations,
246+
cannot be used as an annotation parameter type.
247+
248+
249+
4. Add the `scope` parameter to the `RequiresOptIn` annotation.
250+
```kotlin
251+
@RequiresOptIn(scope = Scope.All)
252+
annotation class PoisonAll(val message: String)
253+
254+
@RequiresOptIn(scope = Scope.Inheritance)
255+
annotation class PoisonOnlySubclasses(val message: String)
256+
```
257+
This design was rejected because it limits the ability to use the same experimental annotation marker for different scopes:
258+
either marking the entire API as unstable or marking only inheritance as unstable.
259+
260+
187261
### Status and timeline
188262

189263
The feature is available since Kotlin 2.0.0 as experimental (it itself requires an opt-in
190264
into `ExperimentalSubclassOptIn`)
191-
and is expected to be promoted to stable in Kotlin 2.1.0.
265+
and stable in Kotlin 2.1.0.

0 commit comments

Comments
 (0)