Skip to content

Commit 970d119

Browse files
authored
Represent Java annotations as interfaces so they can be extended, and disallow various misuses of them (#16260)
Inspired by the work of hrhino in Scala 2 (scala/scala#6869).
2 parents 0298581 + a685a4f commit 970d119

File tree

41 files changed

+212
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+212
-53
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
@@ -969,7 +969,6 @@ class Definitions {
969969

970970
// Annotation base classes
971971
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
972-
@tu lazy val ClassfileAnnotationClass: ClassSymbol = requiredClass("scala.annotation.ClassfileAnnotation")
973972
@tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation")
974973
@tu lazy val RefiningAnnotationClass: ClassSymbol = requiredClass("scala.annotation.RefiningAnnotation")
975974

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/StdNames.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,6 @@ object StdNames {
243243
final val ToString: N = "ToString"
244244
final val Xor: N = "^"
245245

246-
final val ClassfileAnnotation: N = "ClassfileAnnotation"
247246
final val ClassManifest: N = "ClassManifest"
248247
final val Enum: N = "Enum"
249248
final val Group: N = "Group"

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)
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: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -346,25 +346,24 @@ 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

352353
private def addFlag(base: FlagSet, jflag: Int): FlagSet =
353354
if (jflag == 0) base else base | translateFlag(jflag)
354355

355356
private def translateFlags(jflags: Int, baseFlags: FlagSet): FlagSet = {
356-
val nflags =
357-
if ((jflags & JAVA_ACC_ANNOTATION) == 0) jflags
358-
else jflags & ~(JAVA_ACC_ABSTRACT | JAVA_ACC_INTERFACE) // annotations are neither abstract nor interfaces
359357
var res: FlagSet = baseFlags | JavaDefined
360-
res = addFlag(res, nflags & JAVA_ACC_PRIVATE)
361-
res = addFlag(res, nflags & JAVA_ACC_PROTECTED)
362-
res = addFlag(res, nflags & JAVA_ACC_FINAL)
363-
res = addFlag(res, nflags & JAVA_ACC_SYNTHETIC)
364-
res = addFlag(res, nflags & JAVA_ACC_STATIC)
365-
res = addFlag(res, nflags & JAVA_ACC_ENUM)
366-
res = addFlag(res, nflags & JAVA_ACC_ABSTRACT)
367-
res = addFlag(res, nflags & JAVA_ACC_INTERFACE)
358+
res = addFlag(res, jflags & JAVA_ACC_PRIVATE)
359+
res = addFlag(res, jflags & JAVA_ACC_PROTECTED)
360+
res = addFlag(res, jflags & JAVA_ACC_FINAL)
361+
res = addFlag(res, jflags & JAVA_ACC_SYNTHETIC)
362+
res = addFlag(res, jflags & JAVA_ACC_STATIC)
363+
res = addFlag(res, jflags & JAVA_ACC_ENUM)
364+
res = addFlag(res, jflags & JAVA_ACC_ABSTRACT)
365+
res = addFlag(res, jflags & JAVA_ACC_INTERFACE)
366+
res = addFlag(res, jflags & JAVA_ACC_ANNOTATION)
368367
res
369368
}
370369

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,7 @@ class ClassfileParser(
165165
* Updates the read pointer of 'in'. */
166166
def parseParents: List[Type] = {
167167
val superType =
168-
if (isAnnotation) {
169-
in.nextChar
170-
defn.AnnotationClass.typeRef
171-
}
172-
else if (classRoot.symbol == defn.ComparableClass ||
168+
if (classRoot.symbol == defn.ComparableClass ||
173169
classRoot.symbol == defn.JavaCloneableClass ||
174170
classRoot.symbol == defn.JavaSerializableClass) {
175171
// Treat these interfaces as universal traits
@@ -186,7 +182,6 @@ class ClassfileParser(
186182
// Consequently, no best implicit for the "Integral" evidence parameter of "range"
187183
// is found. Previously, this worked because of weak conformance, which has been dropped.
188184

189-
if (isAnnotation) ifaces = defn.ClassfileAnnotationClass.typeRef :: ifaces
190185
superType :: ifaces
191186
}
192187

@@ -845,7 +840,7 @@ class ClassfileParser(
845840

846841
class AnnotConstructorCompleter(classInfo: TempClassInfoType) extends LazyType {
847842
def complete(denot: SymDenotation)(using Context): Unit = {
848-
val attrs = classInfo.decls.toList.filter(sym => sym.isTerm && sym != denot.symbol)
843+
val attrs = classInfo.decls.toList.filter(sym => sym.isTerm && sym != denot.symbol && sym.name != nme.CONSTRUCTOR)
849844
val paramNames = attrs.map(_.name.asTermName)
850845
val paramTypes = attrs.map(_.info.resultType)
851846
denot.info = MethodType(paramNames, paramTypes, classRoot.typeRef)

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@ object JavaParsers {
822822
val iface = atSpan(start, nameOffset) {
823823
TypeDef(
824824
name,
825-
makeTemplate(parents, body, tparams, false)).withMods(mods | Flags.Trait | Flags.JavaInterface | Flags.Abstract)
825+
makeTemplate(parents, body, tparams, false)).withMods(mods | Flags.JavaInterface)
826826
}
827827
addCompanionObject(statics, iface)
828828
}
@@ -858,10 +858,9 @@ object JavaParsers {
858858
}
859859
(statics.toList, members.toList)
860860
}
861-
def annotationParents: List[Select] = List(
862-
scalaAnnotationDot(tpnme.Annotation),
863-
Select(javaLangDot(nme.annotation), tpnme.Annotation),
864-
scalaAnnotationDot(tpnme.ClassfileAnnotation)
861+
def annotationParents: List[Tree] = List(
862+
javaLangObject(),
863+
Select(javaLangDot(nme.annotation), tpnme.Annotation)
865864
)
866865
def annotationDecl(start: Offset, mods: Modifiers): List[Tree] = {
867866
accept(AT)
@@ -877,7 +876,7 @@ object JavaParsers {
877876
List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined))
878877
val templ = makeTemplate(annotationParents, constr :: body, List(), true)
879878
val annot = atSpan(start, nameOffset) {
880-
TypeDef(name, templ).withMods(mods | Flags.Abstract)
879+
TypeDef(name, templ).withMods(mods | Flags.JavaInterface | Flags.JavaAnnotation)
881880
}
882881
addCompanionObject(statics, annot)
883882
}

compiler/src/dotty/tools/dotc/transform/RepeatableAnnotations.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Symbols.defn
1010
import Constants._
1111
import Types._
1212
import Decorators._
13+
import Flags._
1314

1415
import scala.collection.mutable
1516

@@ -33,7 +34,7 @@ class RepeatableAnnotations extends MiniPhase:
3334
val annsByType = stableGroupBy(annotations, _.symbol)
3435
annsByType.flatMap {
3536
case (_, a :: Nil) => a :: Nil
36-
case (sym, anns) if sym.derivesFrom(defn.ClassfileAnnotationClass) =>
37+
case (sym, anns) if sym.is(JavaDefined) =>
3738
sym.getAnnotation(defn.JavaRepeatableAnnot).flatMap(_.argumentConstant(0)) match
3839
case Some(Constant(containerTpe: Type)) =>
3940
val clashingAnns = annsByType.getOrElse(containerTpe.classSymbol, Nil)

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.AnnotationClass)
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`.

0 commit comments

Comments
 (0)