From abe13142bac03a1492e514c9d0660eb9d6f7e269 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 11 Aug 2020 17:40:27 +0200 Subject: [PATCH] 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. --- .../src/dotty/tools/dotc/core/Annotations.scala | 6 ------ .../dotc/core/classfile/ClassfileParser.scala | 17 ++++++++++++++--- tests/pos-java-interop-separate/i9492/A_1.java | 15 +++++++++++++++ tests/pos-java-interop-separate/i9492/B_2.scala | 3 +++ 4 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/pos-java-interop-separate/i9492/A_1.java create mode 100644 tests/pos-java-interop-separate/i9492/B_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 52b515ad0ae8..7e0688dba03b 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -163,12 +163,6 @@ object Annotations { protected var myTree: Tree | (Context ?=> Tree) = (using ctx) => treeFn(using ctx) } - def deferred(atp: Type, args: List[Tree])(using Context): Annotation = - deferred(atp.classSymbol)(New(atp, args)) - - def deferredResolve(atp: Type, args: List[ast.untpd.Tree])(using Context): Annotation = - deferred(atp.classSymbol)(ast.untpd.resolveConstructor(atp, args)) - /** Extractor for child annotations */ object Child { diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 94b40ab84e80..d4d0d702f45f 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -555,14 +555,25 @@ class ClassfileParser( Some(untpd.JavaSeqLiteral(elems, TypeTree())) } case ANNOTATION_TAG => - parseAnnotation(index, skip) map (_.tree) + parseAnnotation(index, skip).map(_.untpdTree) } } + class ClassfileAnnotation(annotType: Type, args: List[untpd.Tree]) extends LazyAnnotation { + protected var mySym: Symbol | (Context ?=> Symbol) = + (using ctx: Context) => annotType.classSymbol + + protected var myTree: Tree | (Context ?=> Tree) = + (using ctx: Context) => untpd.resolveConstructor(annotType, args) + + def untpdTree(using Context): untpd.Tree = + untpd.New(untpd.TypeTree(annotType), List(args)) + } + /** Parse and return a single annotation. If it is malformed, * return None. */ - def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(using Context): Option[Annotation] = try { + def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(using Context): Option[ClassfileAnnotation] = try { val attrType = pool.getType(attrNameIndex) attrType match case tp: TypeRef => @@ -584,7 +595,7 @@ class ClassfileParser( } } if (hasError || skip) None - else Some(Annotation.deferredResolve(attrType, argbuf.toList)) + else Some(ClassfileAnnotation(attrType, argbuf.toList)) } catch { case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found diff --git a/tests/pos-java-interop-separate/i9492/A_1.java b/tests/pos-java-interop-separate/i9492/A_1.java new file mode 100644 index 000000000000..9b4192d7dcf0 --- /dev/null +++ b/tests/pos-java-interop-separate/i9492/A_1.java @@ -0,0 +1,15 @@ +import java.lang.annotation.*; + +@interface BaseClassAnn { + Type[] value(); + @interface Type { + Class value(); + } +} + +@BaseClassAnn({ + @BaseClassAnn.Type(value=A_1.class) +}) +abstract class BaseClass {} + +public class A_1 extends BaseClass {} diff --git a/tests/pos-java-interop-separate/i9492/B_2.scala b/tests/pos-java-interop-separate/i9492/B_2.scala new file mode 100644 index 000000000000..9fa34be7f1d3 --- /dev/null +++ b/tests/pos-java-interop-separate/i9492/B_2.scala @@ -0,0 +1,3 @@ +object Test { + def oops: A_1 = ??? +}