Synchronized to modifier?

> However, using an annotation to add a feature supported in the compiler's standard and that changes the JVM bytecode output seems to me in contradiction to the very purpose of annotations and counter-intuitive.

Well, I think you are using an overly restrictive definition of “purpose of annotations”. Kotlin features a few things other than @Synchronized that would have to be built into the language by your logic:

  • @JvmStatic - sets the ACC_STATIC flag (and alters quite a few things in the bytecode too)
  • @JvmName - sets the name of a Java method
    • @file:JvmName - sets the name of a class that contains top-level functions from the given file
    • @file:JvmMultifileClass - allows to put top-level functions from many files to one class (multifile class)
  • @JvmOverloads - generates Java overloads from a Kotlin function with default parameter values
  • @Volatile, @Strictfp, @Transient (and the infamous @Synchronized) - alter corresponding ACC_* flags
  • @Throws - writes an exception signature to the .class file

My opinion is that adding all these to the language syntax whould be a little bit over the top :)

Note one important common trait in these annotations: they are all handled primarily by the JVM-specific back-end of the compiler, the front-end only cares about checking their applicability (actually, it's a JVM-specific part of the front-end). I.e. these annotations do not really affect the language at all, they are directives to the code generator.

Now, let’s look at Kotlin’s modifiers:

  • abstract, open, final, sealed, override: drive inheritance, obviously affect the language
  • enum: alters the syntax of the declaration (allows enum entry list)
  • annotation: alters the syntax: annotation classes have no bodies
  • inner: alters scoping: members of outer class are accessible
  • private, public, internal, protected: regulate visibility, affect name/overload resolution
  • out, in: affect subtyping
  • vararg: affects use-site syntax, types and overload resolution
  • companion: affects both use-site and declaration-site syntax
  • lateinit: affects the syntax: initializer is forbidden, no definite-assignment checks are performed
  • inline, noinline, crossinline: affect possible usages of local returns etc
  • reified: affects possible arguments and uses of a type parameter inside
  • tailrec: affects allowed contents of a function
  • external: affects syntax: non-abstract function with no body
  • data: affects the set of members of a class, inheritance rules etc
So, all these matter a lot to the front-end of the compiler: some even to the parser, others to the type checker and name resolution algorithms. This, I believe, draws a pretty clear divide between what's a modifier built into the language, and what's merely a platform-specific annotation.