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
Represent Java annotations as interfaces so they can be extended
Previously we treated Java annotations as if they were classes, like Scala
annotations. For example, given
@interface Ann { int foo(); }
we pretended it was defined as:
abstract class Ann(foo: Int) extends java.lang.annotation.Annotation { def foo(): Int }
We take advantage of this to type annotation trees as if they were
new calls, for example `@Ann(1)` is typed as `new Ann(1)`.
Pretending that annotations are classes is fine most of the time and matches
what Scala 2.12 did, but it's problematic because the JVM treats annotations as
interfaces. In practice this was only an issue with code trying to extend
Java annotations, which would either be rejected at compile-time or miscompiled
before this commit.
This commit switches our representation of annotations to be trait-based
instead:
trait Ann(foo: Int) extends java.lang.annotation.Annotation { def foo(): Int }
Classes are then free to extend annotations using the same pattern as in Scala 2.13:
class Foo extends Ann {val annotationType = classOf[Retention]; def foo(): Int = 1}
Notice that we still pretend these traits have constructors, this lets us type
annotation trees in much the same way as before, and crucially it means that
macros that depended on the exact tree shape of annotation trees can continue to
work, as demonstrated by the annot-java-tree test extracted from wartremover.
To prevent miscompilation issues, we disallow passing arguments to the
annotation constructor in `extends` clause.
This change is not fully backwards source compatible: this is illustrated
by the diffs in tests/run/repeatable/Test_1.scala:
-@FirstLevel_0(Array(Plain_0(4), Plain_0(5)))
+@FirstLevel_0(Array(new Plain_0(4), new Plain_0(5)))
Here, FirstLevel_0 takes an array of `Plain_0` annotations as arguments, and in
previous releases of Scala 3 we could put `Plain_0(4)` in this array without
`new`. This is because the compiler generates a "constructor proxy" apply method
for classes, but this no longer works since `Plain_0` is now a trait. While we
could potentially tweak the constructor proxy logic to handle this case, it
seems simpler to require a `new` here, both because Scala 2 does it too and
because it ensures that user code that inspects the annotation tree does not
have to deal with constructor proxies.
The treatment of default arguments to annotations stays unchanged from 85cd1cf.
Fixes#5690. Fixes#12840. Fixes#14199.
0 commit comments