-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Carry over Scalac semantics for Java annotations #5690
Comments
Have tried adding the missing flag to fix the crash? Could you send a PR if that works? |
Yes, but if the correct result is compile error, it would only be a partial fix. So should I do that PR without adding a pos test? Should that PR close this issue? |
Such a PR should reference this issue without closing it. It'd still help. I'd maybe add a test in ==
|
Let constructors of java annotations be methods
Let constructors of java annotations be methods
By JLS 9.6, annotation types are a special kind of interface, and by JLS 9.1.1.1 every interface is abstract. |
*JVM Spec quotes:
|
TLDR: Action plan: carry over Scalac fixes. (See previous commit for what exactly). |
Action plan: kidnap @hrhino and get him to port all his fixes to dotty :). |
You'll never find me!! |
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. The treatment of default arguments to annotations stays unchanged from 85cd1cf. Fixes scala#5690. Fixes scala#12840. Fixes scala#14199.
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 scala#5690. Fixes scala#12840. Fixes scala#14199.
Looks like
ClassfileParser.addAnnotationConstructor.addConstr
is missingFlags.Method
, so the synthetic constructor for a java annotation is created as aval
instead of amethod
.Even with that, instantiating java annotations should better be a compile error.
Test code:
Dotc output:
The text was updated successfully, but these errors were encountered: