Skip to content

Commit 0ea949a

Browse files
committed
Fix #6868: Avoid cycles involving Java annotations
Compute the info of synthesized Java annotation constructors lazily, just like the info of all the other members created by ClassfileParser. Thanks to noti0na1 for the initial investigation and test case!
1 parent 85cd1cf commit 0ea949a

File tree

3 files changed

+42
-35
lines changed

3 files changed

+42
-35
lines changed

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

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -638,42 +638,21 @@ class ClassfileParser(
638638
/** Annotations in Scala are assumed to get all their arguments as constructor
639639
* parameters. For Java annotations we need to fake it by making up the constructor.
640640
*/
641-
def addAnnotationConstructor(classInfo: TempClassInfoType)(implicit ctx: Context): Unit = {
642-
val attrs = classInfo.decls.toList.filter(_.isTerm)
643-
val paramNames = attrs.map(_.name.asTermName)
644-
val paramTypes = attrs.map(_.info.resultType)
645-
646-
def addConstr(ptypes: List[Type]) = {
647-
val mtype = MethodType(paramNames, ptypes, classRoot.typeRef)
648-
val constr = ctx.newSymbol(
649-
owner = classRoot.symbol,
650-
name = nme.CONSTRUCTOR,
651-
flags = Flags.Synthetic | Flags.JavaDefined | Flags.Method,
652-
info = mtype
653-
).entered
654-
}
655-
656-
addConstr(paramTypes)
657-
658-
// The code below added an extra constructor to annotations where the
659-
// last parameter of the constructor is an Array[X] for some X, the
660-
// array was replaced by a vararg argument. Unfortunately this breaks
661-
// inference when doing:
662-
// @Annot(Array())
663-
// The constructor is overloaded so the expected type of `Array()` is
664-
// WildcardType, and the type parameter of the Array apply method gets
665-
// instantiated to `Nothing` instead of `X`.
666-
// I'm leaving this commented out in case we improve inference to make this work.
667-
// Note that if this is reenabled then JavaParser will also need to be modified
668-
// to add the extra constructor (this was not implemented before).
669-
/*
670-
if (paramTypes.nonEmpty)
671-
paramTypes.last match {
672-
case defn.ArrayOf(elemtp) =>
673-
addConstr(paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp))
674-
case _ =>
641+
def addAnnotationConstructor(classInfo: TempClassInfoType)(implicit ctx: Context): Unit =
642+
ctx.newSymbol(
643+
owner = classRoot.symbol,
644+
name = nme.CONSTRUCTOR,
645+
flags = Flags.Synthetic | Flags.JavaDefined | Flags.Method,
646+
info = new AnnotConstructorCompleter(classInfo)
647+
).entered
648+
649+
class AnnotConstructorCompleter(classInfo: TempClassInfoType) extends LazyType {
650+
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
651+
val attrs = classInfo.decls.toList.filter(sym => sym.isTerm && sym != denot.symbol)
652+
val paramNames = attrs.map(_.name.asTermName)
653+
val paramTypes = attrs.map(_.info.resultType)
654+
denot.info = MethodType(paramNames, paramTypes, classRoot.typeRef)
675655
}
676-
*/
677656
}
678657

679658
/** Enter own inner classes in the right scope. It needs the scopes to be set up,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
public @interface MyJava_1 {
2+
3+
public String value() default "MyJava";
4+
5+
public MyClassTypeA typeA();
6+
7+
public MyClassTypeB typeB() default @MyClassTypeB;
8+
9+
public enum MyClassTypeA {
10+
A, B
11+
}
12+
13+
public @interface MyClassTypeB {}
14+
}
15+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@MyJava_1("MyScala1", typeA = MyJava_1.MyClassTypeA.B)
2+
object MyScala {
3+
def a(mj: MyJava_1): Unit = {
4+
println("MyJava")
5+
}
6+
7+
@MyJava_1(typeA = MyJava_1.MyClassTypeA.A)
8+
def b(): Int = 1
9+
10+
@MyJava_1(value = "MyScala2", typeA = MyJava_1.MyClassTypeA.B)
11+
def c(): Int = 2
12+
}
13+

0 commit comments

Comments
 (0)