diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index 61c883e1691f..29c75a99e666 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -317,9 +317,8 @@ trait BCodeHelpers extends BCodeIdiomatic { case e => List(e) } } - for(arg <- flatArgs) { + for arg <- flatArgs do emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) - } arrAnnotV.visitEnd() /* case sb @ ScalaSigBytes(bytes) => @@ -340,6 +339,9 @@ trait BCodeHelpers extends BCodeIdiomatic { val nestedVisitor = av.visitAnnotation(name, desc) emitAssocs(nestedVisitor, assocs, bcodeStore)(innerClasesStore) + case Inlined(_, _, expansion) => + emitArgument(av, name, arg = expansion, bcodeStore)(innerClasesStore) + case t => report.error(em"Annotation argument is not a constant", t.sourcePos) } diff --git a/compiler/test/dotty/tools/AnnotationsTests.scala b/compiler/test/dotty/tools/AnnotationsTests.scala index 3998bf7c93c0..68d56241e4c1 100644 --- a/compiler/test/dotty/tools/AnnotationsTests.scala +++ b/compiler/test/dotty/tools/AnnotationsTests.scala @@ -4,17 +4,20 @@ import vulpix.TestConfiguration import org.junit.Test -import dotc.ast.Trees._ -import dotc.core.Decorators._ -import dotc.core.Contexts._ -import dotc.core.Phases._ -import dotc.core.Types._ -import dotc.core.Symbols._ +import dotc.ast.Trees.* +import dotc.ast.tpd +import dotc.core.Decorators.* +import dotc.core.Contexts.* +import dotc.core.Constants.* +import dotc.core.Phases.* +import dotc.core.Types.* +import dotc.core.Symbols.* import java.io.File -import java.nio.file._ +import java.nio.file.* class AnnotationsTest: + def augmentedClassPath(path: Path) = s"${path}${File.pathSeparator}${TestConfiguration.basicClasspath}" @Test def annotTreeNotErased: Unit = withJavaCompiled( @@ -95,3 +98,31 @@ class AnnotationsTest: val term: TermSymbol = requiredClass("java.lang.invoke.MethodHandle").requiredMethod("invokeExact") assert(term.hasAnnotation(defn.NativeAnnot), i"${term.annotations}") } + + @Test def `inlined annot arg is retrieved`: Unit = + val javaSources = + VirtualJavaSource("Param.java", + """|import java.lang.annotation.*; + |@Inherited @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) + |public @interface Param { String[] value(); }""".stripMargin) + :: Nil + val scalaSources = + """object Bar { inline transparent def bar() = Array("a", "b", "c") }""" + :: "@Param(Bar.bar()) class Foo" + :: Nil + withJavaCompiled(javaSources*): javaOutputDir => + inCompilerContext(augmentedClassPath(javaOutputDir), scalaSources = scalaSources*): + val cls = requiredClass("Foo") + val param = requiredClass("Param") + val annots = cls.annotations + assert(annots.size == 2, i"$annots") // includes SourceFile + val annot = annots.find(_.symbol == param).getOrElse(assert(false, s"Missing $param")) + assert(annot.arguments.size == 1, i"${annot.arguments}") + extension (t: tpd.Tree) + def stripNamedArg = tpd.stripNamedArg(t) + def stripInlined = tpd.stripInlined(t) + annot.arguments(0).stripNamedArg.stripInlined match + case Apply(Apply(_, List(Typed(SeqLiteral(ks, _), _))), _) => + assert(ks.length == 3, i"$ks") + assert(ks.map(_.tpe).collect { case ConstantType(Constant(s: String)) => s } == List("a", "b", "c")) + case bad => assert(false, s"Bad arg $bad") diff --git a/tests/pos/i24894a/Bar_1.scala b/tests/pos/i24894a/Bar_1.scala new file mode 100644 index 000000000000..0cf5b556dc79 --- /dev/null +++ b/tests/pos/i24894a/Bar_1.scala @@ -0,0 +1,10 @@ +object Bar { + import quoted.* + + inline transparent def bar(): Array[String] = ${ macroBar() } + def macroBar()(using Quotes): Expr[Array[String]] = { + val l = Range(0, 200, 10).map(_.toString).map(Expr(_)).toList + '{ Array[String](${ Varargs(l) }*) } + //'{ Array[String]("hello", "world") } + } +} diff --git a/tests/pos/i24894a/Foo_2.scala b/tests/pos/i24894a/Foo_2.scala new file mode 100644 index 000000000000..b11df905a911 --- /dev/null +++ b/tests/pos/i24894a/Foo_2.scala @@ -0,0 +1,2 @@ +@Param(Bar.bar()) class Foo +@Param(Array("hi", "bi")) class Baz diff --git a/tests/pos/i24894a/Param.java b/tests/pos/i24894a/Param.java new file mode 100644 index 000000000000..a3ba5cda00ec --- /dev/null +++ b/tests/pos/i24894a/Param.java @@ -0,0 +1,15 @@ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Param { + + String[] value(); + +} diff --git a/tests/pos/i24894b/Bar_1.scala b/tests/pos/i24894b/Bar_1.scala new file mode 100644 index 000000000000..b95ea490e5af --- /dev/null +++ b/tests/pos/i24894b/Bar_1.scala @@ -0,0 +1,9 @@ +object Bar { + import quoted.* + + transparent inline def bar(): String = ${ macroBar() } + def macroBar()(using Quotes): Expr[String] = { + val s = "hello".reverse + Expr(s) + } +} diff --git a/tests/pos/i24894b/Foo_2.scala b/tests/pos/i24894b/Foo_2.scala new file mode 100644 index 000000000000..6a2a857b4c8a --- /dev/null +++ b/tests/pos/i24894b/Foo_2.scala @@ -0,0 +1 @@ +@Deprecated(since = Bar.bar()) class Foo diff --git a/tests/pos/i24894c/OutputTimeUnit.java b/tests/pos/i24894c/OutputTimeUnit.java new file mode 100644 index 000000000000..687be57ca4ad --- /dev/null +++ b/tests/pos/i24894c/OutputTimeUnit.java @@ -0,0 +1,18 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +@Inherited +@Target({ElementType.METHOD,ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface OutputTimeUnit { + + /** + * @return Time unit to use. + */ + TimeUnit value(); + +} diff --git a/tests/pos/i24894c/i21881.scala b/tests/pos/i24894c/i21881.scala new file mode 100644 index 000000000000..5cf317114a45 --- /dev/null +++ b/tests/pos/i24894c/i21881.scala @@ -0,0 +1,11 @@ +class C: + + //final val Seconds = java.util.concurrent.TimeUnit.SECONDS + //inline val Seconds = java.util.concurrent.TimeUnit.SECONDS // not a literal + inline transparent def Seconds = java.util.concurrent.TimeUnit.SECONDS + + @OutputTimeUnit(Seconds) + def f(): Unit = () + + @OutputTimeUnit(java.util.concurrent.TimeUnit.SECONDS) + def ok(): Unit = ()