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

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,8 @@ 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.is(JavaAnnotation))
1114+
report.error(i"${called.name} must appear without any argument to be a valid class parent because it is a Java annotation", call.srcPos)
11131115
if (caller.is(Trait))
11141116
report.error(i"$caller may not call constructor of $called", call.srcPos)
11151117
else if (called.is(Trait) && !caller.mixins.contains(called))
@@ -1263,6 +1265,23 @@ trait Checking {
12631265
if !Inlines.inInlineMethod && !ctx.isInlineContext then
12641266
report.error(em"$what can only be used in an inline method", pos)
12651267

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+
12661285
/** Check arguments of compiler-defined annotations */
12671286
def checkAnnotArgs(tree: Tree)(using Context): tree.type =
12681287
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)
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: 5 additions & 2 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))
2262+
checkAnnotClass(checkAnnotArgs(typed(annot)))
22632263

22642264
def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit =
22652265
val annot = Annotations.Annotation(tree)
@@ -2595,6 +2595,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
25952595
*/
25962596
def ensureConstrCall(cls: ClassSymbol, parent: Tree, psym: Symbol)(using Context): Tree =
25972597
if parent.isType && !cls.is(Trait) && !cls.is(JavaDefined) && psym.isClass
2598+
// Annotations are represented as traits with constructors, but should
2599+
// never be called as such outside of annotation trees.
2600+
&& !psym.is(JavaAnnotation)
25982601
&& (!psym.is(Trait)
25992602
|| psym.primaryConstructor.info.takesParams && !cls.superClass.isSubClass(psym))
26002603
then typed(untpd.New(untpd.TypedSplice(parent), Nil))
@@ -2669,7 +2672,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
26692672
end typedPackageDef
26702673

26712674
def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = {
2672-
val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef)
2675+
val annot1 = checkAnnotClass(typedExpr(tree.annot))
26732676
val annotCls = Annotations.annotClass(annot1)
26742677
if annotCls == defn.NowarnAnnot then
26752678
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

File renamed without changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public @interface Ann_1 {
2+
int value();
3+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test(x: Ann_1) =
2+
val y: scala.annotation.Annotation = x // error

tests/neg/java-ann-extends/Ann.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public @interface Ann {
2+
int value();
3+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test(x: Ann) =
2+
val y: scala.annotation.Annotation = x // error
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public @interface Ann {
2+
int value();
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Bar extends Ann(1) { // error
2+
def value = 1
3+
def annotationType = classOf[Ann]
4+
}
5+
6+
def test =
7+
// Typer errors
8+
new Ann // error
9+
new Ann(1) {} // error
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public @interface Ann {
2+
int value();
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def test =
2+
// Posttyper errors
3+
new Ann(1) // error
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public @interface Ann {
2+
int value();
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def test =
2+
// Refchecks error
3+
new Ann {} // error
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/neg/repeatable/Test_1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import repeatable._
66
@FirstLevel_0(Array()) // error
77
trait U
88

9-
@FirstLevel_0(Array(Plain_0(4), Plain_0(5)))
10-
@FirstLevel_0(Array(Plain_0(6), Plain_0(7)))
9+
@FirstLevel_0(Array(new Plain_0(4), new Plain_0(5)))
10+
@FirstLevel_0(Array(new Plain_0(6), new Plain_0(7)))
1111
@SecondLevel_0(Array()) // error
1212
trait T
1313

1414
@SecondLevel_0(Array())
1515
@SecondLevel_0(Array()) // error
16-
trait S
16+
trait S

tests/pos/i5690.scala

Lines changed: 0 additions & 8 deletions
This file was deleted.

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)