- Type: Design proposal
- Author: Denis Zharkov
- Contributors: Andrey Breslav
- Status: Submitted
- Prototype: Not started
- Discussion: KEEP-99
- Related proposals: JSR-305 custom nullability qualifiers
Add a separate artifact containing meta-annotations similar to ones from JSR-305.
- We need to put somewhere
@ApplyToTypeArgumentsAnnotation
meta-annotation (see the discussion). - There is a modules-related issue with JSR-305 and Java 9.
- It's worth simplifying the way how JSR-305 nullability meta-annotations are being used
and integrating them with Kotlin-specific meta annotations. Namely,
@UnderMigration
and@ApplyToTypeArguments
.
This section describes proposed semantics of the new annotations and partly the layout of resulting artifact.
Root package for this artifact will be kotlin.annotations.jvm
.
All classes/packages names are assumed to be placed in the package and only their
relative names are mentioned below.
In JSR-305, there is @TypeQualifier
annotation that allows to introduce custom qualifiers (like @Nonnull
).
But that kind of meta-meta level seems to be unnecessary to our needs (at least for now),
the Kotlin compiler supports only nullability qualifier
(and in the nearest future mutability might be supported as well).
So, there will only be fixed number of built-in qualifier annotations:
nullability.Nullable
nullability.NotNull
mutability.Mutable
mutability.ReadOnly
Their target set would be the following: ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE
And semantics when being applied to types is just the same as for analogue
from org.jetbrains.annotations
(see more about types enhancement)
This proposal suggests to introduce meta.Alias
annotation which would affect the compiler
in a similar way that @TypeQualifierNickname
does.
In a basic version its declaration may look like
package kotlin.annotations.jvm.meta
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class Alias(val qualifier: KClass<*>)
and its usages is supposed to look like:
@Alias(kotlin.annotations.jvm.nullability.Nullable::class)
annotation class MyNullable
Thus, applying @MyNullable
should have the same effect as @Nullable
itself has.
Beside it, @MyNullable
may have @UnderMigration
annotation on it that would change its
migration status.
This proposal has two options how default qualifiers semantics can be introduced.
And both of them are assume using special enum class instead of ElementType
that is used as a parameter type for JSR-305 @TypeQualifierDefault
.
It might look like:
enum class ApplicabilityKind {
RETURN_TYPE, VALUE_PARAMETER, FIELD, TYPE_USE, TYPE_ARGUMENT
}
All elements should work just the same as relevant entries from ElementType
do for @TypeQualifierDefault
,
beside TYPE_ARGUMENT
.
The latter one should have the following effect: if a default qualifier is determined
to be applied to some top-level type (using the same logic as for @TypeQualifierDefault
)
and the set of applicability kinds contain TYPE_ARGUMENT
then this qualifier should also be applied
to all of it's type arguments (recursively).
The first option is adding propagateTo: Array<DefaultApplicabilityType>
parameter to meta.Alias
annotation.
Thus, declaration of AllParametersAreNullableByDefault
would look like this:
@Alias(Nullable::class, propagateTo=[ApplicabilityKind.VALUE_PARAMETER])
annotation class AllParametersAreNullableByDefault
Known pros:
- It's nice to have one annotation for all issues.
- Probably, for someone such form of default qualifiers would look nicer than one suggested by JSR-305.
Known cons:
- It would look a little weird when it's used in cases like the one below:
@Alias(Nullable::class)
@UnderMigration(...)
annotation class MyNullable
@Alias(MyNullable::class, propagateTo=[ApplicabilityKind.PARAMETER])
annotation class MyAllParametersAreNullableByDefault
- Also it might be confusing when one alias (probably with non-empty
propagateTo
) references another alias with non-trivialpropagateTo
argument
Another option would be a separate annotation meta.ApplyByDefault
with vararg-parameter
of type DefaultApplicabilityType
:
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class ApplyByDefault(val qualifier: KClass<*>, vararg val elements: ApplicabilityKind)
Basically, it should work just the same as @TypeQualifierDefault
, e.g. it might be used like:
@ApplyByDefault(MyNullable::class)
annotation class MyAllParametersAreNullableByDefault
After revisiting design for meta-annotations it looks like a natural thing to set up a migration status for an annotation with its argument instead of another meta-annotation.
Thus, the idea is to add migrationStatus
parameter to meta.Alias
(and to meta.ApplyByDefault
if we decide to introduce the latter) with default value to be STRICT
.
This argument should be processed by the compiler in the same way as for @UnderMigration
annotation
having the same argument value.
There is already an artifact called kotlin-annotations-jvm that might be the best candidate where the new meta annotations may be placed.
Probably, the annotations that are already there should be moved to a different packages:
kotlin.annotations.jvm.MigrationStatus
->kotlin.annotations.jvm.meta
kotlin.annotations.jvm.Mutable
,kotlin.annotations.jvm.ReadOnly
->kotlin.annotations.jvm.collections
- Should aliasing JSR-305 qualifiers like
javax.annotation.Nonnull
be allowed? - What option is the best to choose for supporting default qualifiers?
- Should there be a
migrationStatus
parameter in@Alias
or migration status should be tracked through@UnderMigration
annotation? - What is the best name for
ApplicabilityKind
enum class? Would it be better to place it inside theApplyByDefault
annotation class (if it will be there)