Skip to content

Commit a685a4f

Browse files
committed
Use a dedicated flag to represent Java annotations
A Java interface that extends java.lang.annotation.Annotation might not be a Java annotation, so to prevent false positives we need to keep track of whether an interface is also an annotation when parsing Java sources and bytecode.
1 parent 3125665 commit a685a4f

File tree

17 files changed

+43
-17
lines changed

17 files changed

+43
-17
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
6161
@threadUnsafe lazy val AnnotationRetentionSourceAttr: TermSymbol = requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("SOURCE")
6262
@threadUnsafe lazy val AnnotationRetentionClassAttr: TermSymbol = requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("CLASS")
6363
@threadUnsafe lazy val AnnotationRetentionRuntimeAttr: TermSymbol = requiredClass("java.lang.annotation.RetentionPolicy").linkedClass.requiredValue("RUNTIME")
64-
@threadUnsafe lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation")
6564

6665
val bCodeAsmCommon: BCodeAsmCommon[int.type] = new BCodeAsmCommon(int)
6766

@@ -415,7 +414,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
415414
arrAnnotV.visitEnd()
416415
} // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape.
417416
*/
418-
case t @ Apply(constr, args) if t.tpe.derivesFrom(JavaAnnotationClass) =>
417+
case t @ Apply(constr, args) if t.tpe.classSymbol.is(JavaAnnotation) =>
419418
val typ = t.tpe.classSymbol.denot.info
420419
val assocs = assocsFromApply(t)
421420
val desc = innerClasesStore.typeDescriptor(typ) // the class descriptor of the nested annotation class

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,6 @@ class Definitions {
968968
@tu lazy val Caps_unsafeBoxFunArg: Symbol = CapsUnsafeModule.requiredMethod("unsafeBoxFunArg")
969969

970970
// Annotation base classes
971-
@tu lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation")
972971
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
973972
@tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation")
974973
@tu lazy val RefiningAnnotationClass: ClassSymbol = requiredClass("scala.annotation.RefiningAnnotation")

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,8 @@ object Flags {
350350
/** Symbol is a method which should be marked ACC_SYNCHRONIZED */
351351
val (_, Synchronized @ _, _) = newFlags(36, "<synchronized>")
352352

353-
/** Symbol is a Java-style varargs method */
354-
val (_, JavaVarargs @ _, _) = newFlags(37, "<varargs>")
353+
/** Symbol is a Java-style varargs method / a Java annotation */
354+
val (_, JavaVarargs @ _, JavaAnnotation @ _) = newFlags(37, "<varargs>", "<java-annotation>")
355355

356356
/** Symbol is a Java default method */
357357
val (_, DefaultMethod @ _, _) = newFlags(38, "<defaultmethod>")
@@ -477,7 +477,7 @@ object Flags {
477477
*/
478478
val AfterLoadFlags: FlagSet = commonFlags(
479479
FromStartFlags, AccessFlags, Final, AccessorOrSealed,
480-
Abstract, LazyOrTrait, SelfName, JavaDefined, Transparent)
480+
Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent)
481481

482482
/** A value that's unstable unless complemented with a Stable flag */
483483
val UnstableValueFlags: FlagSet = Mutable | Method

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ object SymDenotations {
808808

809809
/** Is this a Scala or Java annotation ? */
810810
def isAnnotation(using Context): Boolean =
811-
isClass && (derivesFrom(defn.AnnotationClass) || derivesFrom(defn.JavaAnnotationClass))
811+
isClass && (derivesFrom(defn.AnnotationClass) || is(JavaAnnotation))
812812

813813
/** Is this symbol a class that extends `java.io.Serializable` ? */
814814
def isSerializable(using Context): Boolean =

compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ object ClassfileConstants {
346346
case JAVA_ACC_ENUM => Enum
347347
case JAVA_ACC_ABSTRACT => if (isClass) Abstract else Deferred
348348
case JAVA_ACC_INTERFACE => PureInterfaceCreationFlags | JavaDefined
349+
case JAVA_ACC_ANNOTATION => JavaAnnotation
349350
case _ => EmptyFlags
350351
}
351352

@@ -362,6 +363,7 @@ object ClassfileConstants {
362363
res = addFlag(res, jflags & JAVA_ACC_ENUM)
363364
res = addFlag(res, jflags & JAVA_ACC_ABSTRACT)
364365
res = addFlag(res, jflags & JAVA_ACC_INTERFACE)
366+
res = addFlag(res, jflags & JAVA_ACC_ANNOTATION)
365367
res
366368
}
367369

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,7 @@ object JavaParsers {
876876
List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined))
877877
val templ = makeTemplate(annotationParents, constr :: body, List(), true)
878878
val annot = atSpan(start, nameOffset) {
879-
TypeDef(name, templ).withMods(mods | Flags.JavaInterface)
879+
TypeDef(name, templ).withMods(mods | Flags.JavaInterface | Flags.JavaAnnotation)
880880
}
881881
addCompanionObject(statics, annot)
882882
}

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ trait Applications extends Compatibility {
547547

548548
/** Is `sym` a constructor of a Java-defined annotation? */
549549
def isJavaAnnotConstr(sym: Symbol): Boolean =
550-
sym.is(JavaDefined) && sym.isConstructor && sym.owner.derivesFrom(defn.JavaAnnotationClass)
550+
sym.is(JavaDefined) && sym.isConstructor && sym.owner.is(JavaAnnotation)
551551

552552
/** Match re-ordered arguments against formal parameters
553553
* @param n The position of the first parameter in formals in `methType`.

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@ trait Checking {
11101110
def checkParentCall(call: Tree, caller: ClassSymbol)(using Context): Unit =
11111111
if (!ctx.isAfterTyper) {
11121112
val called = call.tpe.classSymbol
1113-
if (called.derivesFrom(defn.JavaAnnotationClass))
1113+
if (called.is(JavaAnnotation))
11141114
report.error(i"${called.name} must appear without any argument to be a valid class parent because it is a Java annotation", call.srcPos)
11151115
if (caller.is(Trait))
11161116
report.error(i"$caller may not call constructor of $called", call.srcPos)
@@ -1265,6 +1265,23 @@ trait Checking {
12651265
if !Inlines.inInlineMethod && !ctx.isInlineContext then
12661266
report.error(em"$what can only be used in an inline method", pos)
12671267

1268+
/** Check that the class corresponding to this tree is either a Scala or Java annotation.
1269+
*
1270+
* @return The original tree or an error tree in case `tree` isn't a valid
1271+
* annotation or already an error tree.
1272+
*/
1273+
def checkAnnotClass(tree: Tree)(using Context): Tree =
1274+
if tree.tpe.isError then
1275+
return tree
1276+
val cls = Annotations.annotClass(tree)
1277+
if cls.is(JavaDefined) then
1278+
if !cls.is(JavaAnnotation) then
1279+
errorTree(tree, em"$cls is not a valid Java annotation: it was not declared with `@interface`")
1280+
else tree
1281+
else if !cls.derivesFrom(defn.AnnotationClass) then
1282+
errorTree(tree, em"$cls is not a valid Scala annotation: it does not extend `scala.annotation.Annotation`")
1283+
else tree
1284+
12681285
/** Check arguments of compiler-defined annotations */
12691286
def checkAnnotArgs(tree: Tree)(using Context): tree.type =
12701287
val cls = Annotations.annotClass(tree)

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ class Namer { typer: Typer =>
833833
if (cls eq sym)
834834
report.error("An annotation class cannot be annotated with iself", annotTree.srcPos)
835835
else {
836-
val ann = Annotation.deferred(cls)(typedAheadAnnotation(annotTree)(using annotCtx))
836+
val ann = Annotation.deferred(cls)(typedAheadExpr(annotTree)(using annotCtx))
837837
sym.addAnnotation(ann)
838838
}
839839
}
@@ -1618,9 +1618,6 @@ class Namer { typer: Typer =>
16181618
def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(using Context): tpd.Tree =
16191619
typedAhead(tree, typer.typedExpr(_, pt))
16201620

1621-
def typedAheadAnnotation(tree: Tree)(using Context): tpd.Tree =
1622-
typedAheadExpr(tree, defn.AnnotationClass.typeRef | defn.JavaAnnotationClass.typeRef)
1623-
16241621
def typedAheadAnnotationClass(tree: Tree)(using Context): Symbol = tree match {
16251622
case Apply(fn, _) => typedAheadAnnotationClass(fn)
16261623
case TypeApply(fn, _) => typedAheadAnnotationClass(fn)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,7 +2259,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
22592259
}
22602260

22612261
def typedAnnotation(annot: untpd.Tree)(using Context): Tree =
2262-
checkAnnotArgs(typed(annot, defn.AnnotationClass.typeRef | defn.JavaAnnotationClass.typeRef))
2262+
checkAnnotClass(checkAnnotArgs(typed(annot)))
22632263

22642264
def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit =
22652265
val annot = Annotations.Annotation(tree)
@@ -2597,7 +2597,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
25972597
if parent.isType && !cls.is(Trait) && !cls.is(JavaDefined) && psym.isClass
25982598
// Annotations are represented as traits with constructors, but should
25992599
// never be called as such outside of annotation trees.
2600-
&& !psym.derivesFrom(defn.JavaAnnotationClass)
2600+
&& !psym.is(JavaAnnotation)
26012601
&& (!psym.is(Trait)
26022602
|| psym.primaryConstructor.info.takesParams && !cls.superClass.isSubClass(psym))
26032603
then typed(untpd.New(untpd.TypedSplice(parent), Nil))
@@ -2672,7 +2672,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
26722672
end typedPackageDef
26732673

26742674
def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = {
2675-
val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef | defn.JavaAnnotationClass.typeRef)
2675+
val annot1 = checkAnnotClass(typedExpr(tree.annot))
26762676
val annotCls = Annotations.annotClass(annot1)
26772677
if annotCls == defn.NowarnAnnot then
26782678
registerNowarn(annot1, tree)

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,6 +2765,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27652765
def Invisible: Flags = dotc.core.Flags.Invisible
27662766
def JavaDefined: Flags = dotc.core.Flags.JavaDefined
27672767
def JavaStatic: Flags = dotc.core.Flags.JavaStatic
2768+
def JavaAnnotation: Flags = dotc.core.Flags.JavaAnnotation
27682769
def Lazy: Flags = dotc.core.Flags.Lazy
27692770
def Local: Flags = dotc.core.Flags.Local
27702771
def Macro: Flags = dotc.core.Flags.Macro

library/src/scala/quoted/Quotes.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4278,6 +4278,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
42784278
/** Is implemented as a Java static */
42794279
def JavaStatic: Flags
42804280

4281+
/** Is this an annotation defined in Java */
4282+
@experimental def JavaAnnotation: Flags
4283+
42814284
/** Is this symbol `lazy` */
42824285
def Lazy: Flags
42834286

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface FakeAnn_1 extends java.lang.annotation.Annotation { }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@FakeAnn_1 def test = // error
2+
(1: @FakeAnn_1) // error
3+

tests/neg/java-fake-ann/FakeAnn.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface FakeAnn extends java.lang.annotation.Annotation { }

tests/neg/java-fake-ann/Test.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@FakeAnn def test = // error
2+
(1: @FakeAnn) // error

tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ val experimentalDefinitionInLibrary = Set(
6262
//// New APIs: Quotes
6363
// Can be stabilized in 3.3.0 (unsure) or later
6464
"scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings",
65+
"scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation",
6566
// Cant be stabilized yet.
6667
// Need newClass variant that can add constructor parameters.
6768
// Need experimental annotation macros to check that design works.

0 commit comments

Comments
 (0)