Skip to content

Commit 5095033

Browse files
committed
Fix #9492: Avoid forcing nested annotation
Java allows annotations inside arrays inside annotations, when parsing the outer annotation, we need to produce an untyped tree for the array and therefore the nested annotations. Previously this was done using `Annotation#tree` which actually produces a typed tree, this works since typed trees can be used as untyped tree but it requires forcing the content of the nested annotation which can lead to cycles. Instead, we now use a dedicated subclass of Annotation with an extra `untpdTree` method we use for the purpose of generating a tree for the nested annotation without forcing anything.
1 parent dfcc0b1 commit 5095033

File tree

4 files changed

+31
-9
lines changed

4 files changed

+31
-9
lines changed

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

-6
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,6 @@ object Annotations {
163163
protected var myTree: Tree | (Context ?=> Tree) = (using ctx) => treeFn(using ctx)
164164
}
165165

166-
def deferred(atp: Type, args: List[Tree])(using Context): Annotation =
167-
deferred(atp.classSymbol)(New(atp, args))
168-
169-
def deferredResolve(atp: Type, args: List[ast.untpd.Tree])(using Context): Annotation =
170-
deferred(atp.classSymbol)(ast.untpd.resolveConstructor(atp, args))
171-
172166
/** Extractor for child annotations */
173167
object Child {
174168

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

+13-3
Original file line numberDiff line numberDiff line change
@@ -555,14 +555,24 @@ class ClassfileParser(
555555
Some(untpd.JavaSeqLiteral(elems, TypeTree()))
556556
}
557557
case ANNOTATION_TAG =>
558-
parseAnnotation(index, skip) map (_.tree)
558+
parseAnnotation(index, skip).map(_.untpdTree)
559559
}
560560
}
561561

562+
class ClassfileAnnotation(annotClass: Symbol, annotType: Type, args: List[untpd.Tree]) extends LazyAnnotation {
563+
protected var mySym: Symbol | (Context ?=> Symbol) = annotClass
564+
565+
protected var myTree: Tree | (Context ?=> Tree) =
566+
(using ctx: Context) => untpd.resolveConstructor(annotType, args)
567+
568+
def untpdTree(using Context): untpd.Tree =
569+
untpd.New(untpd.TypeTree(annotType), List(args))
570+
}
571+
562572
/** Parse and return a single annotation. If it is malformed,
563573
* return None.
564574
*/
565-
def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(using Context): Option[Annotation] = try {
575+
def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(using Context): Option[ClassfileAnnotation] = try {
566576
val attrType = pool.getType(attrNameIndex)
567577
attrType match
568578
case tp: TypeRef =>
@@ -584,7 +594,7 @@ class ClassfileParser(
584594
}
585595
}
586596
if (hasError || skip) None
587-
else Some(Annotation.deferredResolve(attrType, argbuf.toList))
597+
else Some(ClassfileAnnotation(attrType.classSymbol, attrType, argbuf.toList))
588598
}
589599
catch {
590600
case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import java.lang.annotation.*;
2+
3+
@interface BaseClassAnn {
4+
Type[] value();
5+
@interface Type {
6+
Class<?> value();
7+
}
8+
}
9+
10+
@BaseClassAnn({
11+
@BaseClassAnn.Type(value=A_1.class)
12+
})
13+
abstract class BaseClass {}
14+
15+
public class A_1 extends BaseClass {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test {
2+
def oops: A_1 = ???
3+
}

0 commit comments

Comments
 (0)