forked from scala/scala3
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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. The treatment of default arguments to annotations stays unchanged from 85cd1cf. Fixes scala#5690. Fixes scala#12840. Fixes scala#14199.
- Loading branch information
Showing
23 changed files
with
163 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
public @interface Ann { | ||
int value(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
class Bar extends Ann(1) { // error | ||
def value = 1 | ||
def annotationType = classOf[Ann] | ||
} | ||
|
||
def test = | ||
// Typer errors | ||
new Ann // error | ||
new Ann(1) {} // error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
public @interface Ann { | ||
int value(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
def test = | ||
// Posttyper errors | ||
new Ann(1) // error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
public @interface Ann { | ||
int value(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
def test = | ||
// Refchecks error | ||
new Ann {} // error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import scala.quoted.* | ||
|
||
inline def checkSuppressWarnings[T]: Unit = ${ checkSuppressWarningsImpl[T] } | ||
|
||
def checkSuppressWarningsImpl[T: Type](using Quotes): Expr[Unit] = | ||
import quotes.reflect.* | ||
val SuppressWarningsSymbol = TypeTree.of[SuppressWarnings].symbol | ||
val sym = TypeRepr.of[T].typeSymbol | ||
// Imitate what wartremover does, so we can avoid unintentionally breaking it: | ||
// https://github.com/wartremover/wartremover/blob/fb18e6eafe9a47823e04960aaf4ec7a9293719ef/core/src/main/scala-3/org/wartremover/WartUniverse.scala#L63-L77 | ||
val actualArgs = sym | ||
.getAnnotation(SuppressWarningsSymbol) | ||
.collect { | ||
case Apply( | ||
Select(_, "<init>"), | ||
Apply(Apply(_, Typed(Repeated(values, _), _) :: Nil), Apply(_, _ :: Nil) :: Nil) :: Nil | ||
) => | ||
// "-Yexplicit-nulls" | ||
// https://github.com/wartremover/wartremover/issues/660 | ||
values.collect { case Literal(StringConstant(str)) => | ||
str | ||
} | ||
} | ||
.toList | ||
.flatten | ||
val expectedArgs = List("a", "b") | ||
assert(actualArgs == expectedArgs, | ||
s"Expected $expectedArgs arguments for SuppressWarnings annotation of $sym but got $actualArgs") | ||
'{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@SuppressWarnings(Array("a", "b")) class Foo | ||
|
||
@main def Test = | ||
checkSuppressWarnings[Foo] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
public @interface Ann_1 { | ||
int bar() default 1; | ||
int baz() default 2; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// scalajs: --skip | ||
|
||
class Foo extends Ann_1 { | ||
override def bar = 3 | ||
override def baz = 4 | ||
def annotationType = classOf[Ann_1] | ||
} | ||
|
||
object Test { | ||
def main(args: Array[String]): Unit = { | ||
val x = new Foo | ||
val y: Ann_1 = x | ||
val z: Int @Ann_1(1) = 1 | ||
val zz: Int @Ann_1() = 1 | ||
// val x: scala.annotation.Annotation = new Ann { | ||
// // val x: java.lang.annotation.Annotation = new Ann { | ||
// def annotationType = classOf[Ann] | ||
// } | ||
// println(x) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
public @interface Ann { | ||
int bar() default 1; | ||
int baz() default 2; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// scalajs: --skip | ||
|
||
class Foo extends Ann { | ||
override def bar = 3 | ||
override def baz = 4 | ||
def annotationType = classOf[Ann] | ||
} | ||
|
||
object Test { | ||
def main(args: Array[String]): Unit = { | ||
val x = new Foo | ||
val y: Ann = x | ||
val z: Int @Ann(1) = 1 | ||
val zz: Int @Ann() = 1 | ||
// val x: scala.annotation.Annotation = new Ann { | ||
// // val x: java.lang.annotation.Annotation = new Ann { | ||
// def annotationType = classOf[Ann] | ||
// } | ||
// println(x) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// scalajs: --skip | ||
|
||
class Deprecation extends Deprecated { | ||
final val annotationType = classOf[Deprecated] | ||
|
||
def forRemoval(): Boolean = false | ||
def since(): String = "" | ||
} | ||
|
||
class Suppression extends SuppressWarnings { | ||
final val annotationType = classOf[SuppressWarnings] | ||
|
||
def value = Array("unchecked") | ||
} | ||
|
||
class Retention(runtime: Boolean) extends java.lang.annotation.Retention { | ||
final val annotationType = classOf[Retention] | ||
|
||
def value = | ||
if (runtime) java.lang.annotation.RetentionPolicy.RUNTIME | ||
else java.lang.annotation.RetentionPolicy.SOURCE | ||
} | ||
|
||
object Test extends App { | ||
new Deprecation | ||
new Suppression | ||
new Retention(true) | ||
} |