From 6d3ee4ad27c0761419070c12f8db36151899ffd3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 30 Nov 2022 17:12:22 +0000 Subject: [PATCH 01/16] Support use-site meta-annotations Co-Authored-By: Guillaume Martres --- .../dotty/tools/dotc/core/Annotations.scala | 13 +++++++ .../tools/dotc/transform/PostTyper.scala | 11 +++--- .../dotty/tools/dotc/transform/SymUtils.scala | 2 +- tests/run/i12492.check | 9 +++++ tests/run/i12492/MyColumnBase.java | 7 ++++ tests/run/i12492/MyTable.scala | 36 +++++++++++++++++++ 6 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 tests/run/i12492.check create mode 100644 tests/run/i12492/MyColumnBase.java create mode 100644 tests/run/i12492/MyTable.scala diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index aa8ead280bbf..51f791456eb1 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -87,6 +87,19 @@ object Annotations { def sameAnnotation(that: Annotation)(using Context): Boolean = symbol == that.symbol && tree.sameTree(that.tree) + def hasOneOfMetaAnnotation(metaSyms: Symbol*)(using Context): Boolean = + def recTp(tp: Type): Boolean = tp.dealiasKeepAnnots match + case AnnotatedType(parent, metaAnnot) => metaSyms.exists(metaAnnot.matches) || recTp(parent) + case _ => false + def rec(tree: Tree): Boolean = methPart(tree) match + case New(tpt) => rec(tpt) + case Select(qual, _) => rec(qual) + case Annotated(arg, metaAnnot) => metaSyms.exists(metaAnnot.tpe.classSymbol.derivesFrom) || rec(arg) + case t @ Ident(_) => recTp(t.tpe) + case Typed(expr, _) => rec(expr) + case _ => false + metaSyms.exists(symbol.hasAnnotation) || rec(tree) + /** Operations for hash-consing, can be overridden */ def hash: Int = System.identityHashCode(this) def eql(that: Annotation) = this eq that diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index a669be3d1e3c..ad75b79be8ae 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package transform import dotty.tools.dotc.ast.{Trees, tpd, untpd, desugar} @@ -186,12 +187,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase private def removeUnwantedAnnotations(sym: Symbol, metaAnnotSym: Symbol, metaAnnotSymBackup: Symbol, keepIfNoRelevantAnnot: Boolean)(using Context): Unit = def shouldKeep(annot: Annotation): Boolean = - val annotSym = annot.symbol - annotSym.hasAnnotation(metaAnnotSym) - || annotSym.hasAnnotation(metaAnnotSymBackup) - || (keepIfNoRelevantAnnot && { - !annotSym.annotations.exists(metaAnnot => defn.FieldAccessorMetaAnnots.contains(metaAnnot.symbol)) - }) + annot.hasOneOfMetaAnnotation(metaAnnotSym, metaAnnotSymBackup) + || keepIfNoRelevantAnnot && !annot.hasOneOfMetaAnnotation(defn.FieldAccessorMetaAnnots.toList*) if sym.annotations.nonEmpty then sym.filterAnnotations(shouldKeep(_)) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 6010fe2a2a44..d6312f0c07d6 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -271,7 +271,7 @@ object SymUtils: self.isAllOf(EnumCase, butNot = JavaDefined) def annotationsCarrying(meta: ClassSymbol)(using Context): List[Annotation] = - self.annotations.filter(_.symbol.hasAnnotation(meta)) + self.annotations.filter(_.hasOneOfMetaAnnotation(meta)) def withAnnotationsCarrying(from: Symbol, meta: ClassSymbol)(using Context): self.type = { self.addAnnotations(from.annotationsCarrying(meta)) diff --git a/tests/run/i12492.check b/tests/run/i12492.check new file mode 100644 index 000000000000..41243297502f --- /dev/null +++ b/tests/run/i12492.check @@ -0,0 +1,9 @@ +inspecting field brandName +@MyColumnBase(name="BRAND_NAME") +inspecting field companyName +@MyColumnBase(name="COMPANY_NAME") +inspecting method brandName +inspecting method companyName +inspecting constructor MyTable +inspecting param brandName +inspecting param companyName diff --git a/tests/run/i12492/MyColumnBase.java b/tests/run/i12492/MyColumnBase.java new file mode 100644 index 000000000000..e0253649a1f3 --- /dev/null +++ b/tests/run/i12492/MyColumnBase.java @@ -0,0 +1,7 @@ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface MyColumnBase { + String name() default ""; +} diff --git a/tests/run/i12492/MyTable.scala b/tests/run/i12492/MyTable.scala new file mode 100644 index 000000000000..737998931326 --- /dev/null +++ b/tests/run/i12492/MyTable.scala @@ -0,0 +1,36 @@ +import scala.annotation.meta.field + +type MyColumn = MyColumnBase @field + +class MyTable( + @(MyColumnBase @field)(name="BRAND_NAME") + val brandName: String, + @MyColumn(name="COMPANY_NAME") + val companyName: String, +) + +object Test: + def main(args: Array[String]): Unit = + val clasz = classOf[MyTable] + + for (m <- clasz.getDeclaredFields.sortBy(_.getName)) { + m.setAccessible(true) + println(s"inspecting field ${m.getName}") + for a <- m.getAnnotations().sortBy(_.toString) do + println(a) + } + + for (m <- clasz.getDeclaredMethods.sortBy(_.getName)) { + m.setAccessible(true) + println(s"inspecting method ${m.getName}") + for a <- m.getAnnotations().sortBy(_.toString) do + println(a) + } + + for c <- clasz.getDeclaredConstructors.sortBy(_.getName) do + c.setAccessible(true) + println(s"inspecting constructor ${c.getName}") + for p <- c.getParameters.sortBy(_.getName) do + println(s"inspecting param ${p.getName}") + for a <- p.getAnnotations.sortBy(_.toString) do + println(s"annotation $a") From 6d5ccbb2818f65de7303e2ecbc4f213482e8e0e9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 30 Nov 2022 23:21:50 +0000 Subject: [PATCH 02/16] Fix bean-meta annotation support --- .../dotty/tools/dotc/core/Definitions.scala | 2 ++ .../tools/dotc/transform/BeanProperties.scala | 13 +++++++--- tests/run/i15318.check | 8 ++++++ tests/run/i15318/JsonProperty.java | 7 ++++++ tests/run/i15318/Test.scala | 25 +++++++++++++++++++ tests/run/i15318/TestBeanProperty.scala | 10 ++++++++ 6 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 tests/run/i15318.check create mode 100644 tests/run/i15318/JsonProperty.java create mode 100644 tests/run/i15318/Test.scala create mode 100644 tests/run/i15318/TestBeanProperty.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index bad796df2bd5..72d7749bb495 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1021,6 +1021,8 @@ class Definitions { @tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance") @tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile") @tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns") + @tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter") + @tu lazy val BeanSetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanSetter") @tu lazy val FieldMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.field") @tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter") @tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param") diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index 0d464d319848..3732777b082f 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -34,7 +34,10 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(Nil, valDef.denot.info), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - meth.addAnnotations(valDef.symbol.annotations) + val annots = valDef.symbol.annotations.filter { a => + a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) | !a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) + } + meth.addAnnotations(annots) val body: Tree = ref(valDef.symbol) DefDef(meth, body) @@ -48,9 +51,11 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(valDef.name :: Nil, valDef.denot.info :: Nil, defn.UnitType), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - meth.addAnnotations(valDef.symbol.annotations) - def body(params: List[List[Tree]]): Tree = Assign(ref(valDef.symbol), params.head.head) - DefDef(meth, body) + val annots = valDef.symbol.annotations.filter { a => + a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) | !a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) + } + meth.addAnnotations(annots) + DefDef(meth, (params: List[List[Tree]]) => Assign(ref(valDef.symbol), params.head.head)) } def prefixedName(prefix: String, valName: Name) = diff --git a/tests/run/i15318.check b/tests/run/i15318.check new file mode 100644 index 000000000000..351fda72f6f7 --- /dev/null +++ b/tests/run/i15318.check @@ -0,0 +1,8 @@ +inspecting field value +@JsonProperty(value="REAL_VALUE") +inspecting method getValue +@JsonProperty(value="REAL_VALUE") +inspecting method setValue +inspecting method value +inspecting method value_$eq +inspecting constructor TestBeanProperty diff --git a/tests/run/i15318/JsonProperty.java b/tests/run/i15318/JsonProperty.java new file mode 100644 index 000000000000..0d96489aeaf0 --- /dev/null +++ b/tests/run/i15318/JsonProperty.java @@ -0,0 +1,7 @@ +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface JsonProperty { + String value() default ""; +} diff --git a/tests/run/i15318/Test.scala b/tests/run/i15318/Test.scala new file mode 100644 index 000000000000..47b9a3908d2e --- /dev/null +++ b/tests/run/i15318/Test.scala @@ -0,0 +1,25 @@ +object Test: + def main(args: Array[String]): Unit = + val clasz = classOf[TestBeanProperty] + + for (m <- clasz.getDeclaredFields.sortBy(_.getName)) { + m.setAccessible(true) + println(s"inspecting field ${m.getName}") + for a <- m.getAnnotations().sortBy(_.toString) do + println(a) + } + + for (m <- clasz.getDeclaredMethods.sortBy(_.getName)) { + m.setAccessible(true) + println(s"inspecting method ${m.getName}") + for a <- m.getAnnotations().sortBy(_.toString) do + println(a) + } + + for c <- clasz.getDeclaredConstructors.sortBy(_.getName) do + c.setAccessible(true) + println(s"inspecting constructor ${c.getName}") + for p <- c.getParameters.sortBy(_.getName) do + println(s"inspecting param ${p.getName}") + for a <- p.getAnnotations.sortBy(_.toString) do + println(s"annotation $a") diff --git a/tests/run/i15318/TestBeanProperty.scala b/tests/run/i15318/TestBeanProperty.scala new file mode 100644 index 000000000000..3638b567ed7a --- /dev/null +++ b/tests/run/i15318/TestBeanProperty.scala @@ -0,0 +1,10 @@ +import scala.annotation.meta.beanGetter +import scala.beans.BeanProperty + +class TestBeanProperty { + + @(JsonProperty @beanGetter)(value = "REAL_VALUE") + @BeanProperty + var value: String = _ + +} From 1c644d7dd2153ec7eb54a271f18133c941dd2276 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Dec 2022 10:01:08 +0000 Subject: [PATCH 03/16] Fix BeanProperties positions --- compiler/src/dotty/tools/dotc/transform/BeanProperties.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/Pickler.scala | 4 +++- compiler/src/dotty/tools/dotc/util/Spans.scala | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index 3732777b082f..4d2ab8da6533 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -39,7 +39,7 @@ class BeanProperties(thisPhase: DenotTransformer): } meth.addAnnotations(annots) val body: Tree = ref(valDef.symbol) - DefDef(meth, body) + DefDef(meth, body).withSpan(meth.span) def maybeGenerateSetter(valDef: ValDef, annot: Annotation)(using Context): Option[Tree] = Option.when(valDef.denot.asSymDenotation.flags.is(Mutable)) { @@ -55,7 +55,7 @@ class BeanProperties(thisPhase: DenotTransformer): a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) | !a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) } meth.addAnnotations(annots) - DefDef(meth, (params: List[List[Tree]]) => Assign(ref(valDef.symbol), params.head.head)) + DefDef(meth, (params: List[List[Tree]]) => Assign(ref(valDef.symbol), params.head.head)).withSpan(meth.span) } def prefixedName(prefix: String, valName: Name) = diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index b862ca7fe01e..52cd59c9483a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package transform import core._ @@ -147,6 +148,7 @@ class Pickler extends Phase { if unequal then output("before-pickling.txt", previous) output("after-pickling.txt", unpickled) + //sys.process.Process("diff -u before-pickling.txt after-pickling.txt").! report.error(em"""pickling difference for $cls in ${cls.source}, for details: | | diff before-pickling.txt after-pickling.txt""") diff --git a/compiler/src/dotty/tools/dotc/util/Spans.scala b/compiler/src/dotty/tools/dotc/util/Spans.scala index baf2cfa121b0..ba537e9aec01 100644 --- a/compiler/src/dotty/tools/dotc/util/Spans.scala +++ b/compiler/src/dotty/tools/dotc/util/Spans.scala @@ -182,6 +182,7 @@ object Spans { assert(isSpan) if (this == NoCoord) NoSpan else Span(-1 - encoding) } + override def toString = if isSpan then s"$toSpan" else s"Coord(idx=$toIndex)" } /** An index coordinate */ From 78a433ffb20eac5e17b778058685889631e83447 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Dec 2022 12:25:27 +0000 Subject: [PATCH 04/16] Don't depend on JVM's Annotation toString impl --- tests/run/i12492.check | 4 ++-- tests/run/i12492/MyTable.scala | 26 -------------------------- tests/run/i12492/Test.scala | 25 +++++++++++++++++++++++++ tests/run/i15318.check | 4 ++-- tests/run/i15318/Test.scala | 20 ++++++++++---------- 5 files changed, 39 insertions(+), 40 deletions(-) create mode 100644 tests/run/i12492/Test.scala diff --git a/tests/run/i12492.check b/tests/run/i12492.check index 41243297502f..d251ced8beb1 100644 --- a/tests/run/i12492.check +++ b/tests/run/i12492.check @@ -1,7 +1,7 @@ inspecting field brandName -@MyColumnBase(name="BRAND_NAME") +interface MyColumnBase inspecting field companyName -@MyColumnBase(name="COMPANY_NAME") +interface MyColumnBase inspecting method brandName inspecting method companyName inspecting constructor MyTable diff --git a/tests/run/i12492/MyTable.scala b/tests/run/i12492/MyTable.scala index 737998931326..a6556c45b2b8 100644 --- a/tests/run/i12492/MyTable.scala +++ b/tests/run/i12492/MyTable.scala @@ -8,29 +8,3 @@ class MyTable( @MyColumn(name="COMPANY_NAME") val companyName: String, ) - -object Test: - def main(args: Array[String]): Unit = - val clasz = classOf[MyTable] - - for (m <- clasz.getDeclaredFields.sortBy(_.getName)) { - m.setAccessible(true) - println(s"inspecting field ${m.getName}") - for a <- m.getAnnotations().sortBy(_.toString) do - println(a) - } - - for (m <- clasz.getDeclaredMethods.sortBy(_.getName)) { - m.setAccessible(true) - println(s"inspecting method ${m.getName}") - for a <- m.getAnnotations().sortBy(_.toString) do - println(a) - } - - for c <- clasz.getDeclaredConstructors.sortBy(_.getName) do - c.setAccessible(true) - println(s"inspecting constructor ${c.getName}") - for p <- c.getParameters.sortBy(_.getName) do - println(s"inspecting param ${p.getName}") - for a <- p.getAnnotations.sortBy(_.toString) do - println(s"annotation $a") diff --git a/tests/run/i12492/Test.scala b/tests/run/i12492/Test.scala new file mode 100644 index 000000000000..39313af4092b --- /dev/null +++ b/tests/run/i12492/Test.scala @@ -0,0 +1,25 @@ +object Test: + def main(args: Array[String]): Unit = + val cls = classOf[MyTable] + + for (m <- cls.getDeclaredFields.sortBy(_.getName)) { + m.setAccessible(true) + println(s"inspecting field ${m.getName}") + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + println(a.annotationType) + } + + for (m <- cls.getDeclaredMethods.sortBy(_.getName)) { + m.setAccessible(true) + println(s"inspecting method ${m.getName}") + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + println(a.annotationType) + } + + for c <- cls.getDeclaredConstructors.sortBy(_.getName) do + c.setAccessible(true) + println(s"inspecting constructor ${c.getName}") + for p <- c.getParameters.sortBy(_.getName) do + println(s"inspecting param ${p.getName}") + for a <- p.getAnnotations.sortBy(_.annotationType.toString) do + println(a.annotationType) diff --git a/tests/run/i15318.check b/tests/run/i15318.check index 351fda72f6f7..8442a56a9d69 100644 --- a/tests/run/i15318.check +++ b/tests/run/i15318.check @@ -1,7 +1,7 @@ inspecting field value -@JsonProperty(value="REAL_VALUE") +interface JsonProperty inspecting method getValue -@JsonProperty(value="REAL_VALUE") +interface JsonProperty inspecting method setValue inspecting method value inspecting method value_$eq diff --git a/tests/run/i15318/Test.scala b/tests/run/i15318/Test.scala index 47b9a3908d2e..192d22975261 100644 --- a/tests/run/i15318/Test.scala +++ b/tests/run/i15318/Test.scala @@ -1,25 +1,25 @@ object Test: def main(args: Array[String]): Unit = - val clasz = classOf[TestBeanProperty] + val cls = classOf[TestBeanProperty] - for (m <- clasz.getDeclaredFields.sortBy(_.getName)) { + for (m <- cls.getDeclaredFields.sortBy(_.getName)) { m.setAccessible(true) println(s"inspecting field ${m.getName}") - for a <- m.getAnnotations().sortBy(_.toString) do - println(a) + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + println(a.annotationType) } - for (m <- clasz.getDeclaredMethods.sortBy(_.getName)) { + for (m <- cls.getDeclaredMethods.sortBy(_.getName)) { m.setAccessible(true) println(s"inspecting method ${m.getName}") - for a <- m.getAnnotations().sortBy(_.toString) do - println(a) + for a <- m.getAnnotations().sortBy(_.annotationType.toString) do + println(a.annotationType) } - for c <- clasz.getDeclaredConstructors.sortBy(_.getName) do + for c <- cls.getDeclaredConstructors.sortBy(_.getName) do c.setAccessible(true) println(s"inspecting constructor ${c.getName}") for p <- c.getParameters.sortBy(_.getName) do println(s"inspecting param ${p.getName}") - for a <- p.getAnnotations.sortBy(_.toString) do - println(s"annotation $a") + for a <- p.getAnnotations.sortBy(_.annotationType.toString) do + println(a.annotationType) From 48a7d112d91f6d86c3916f33e7d65b61ef380d56 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 1 Dec 2022 13:05:12 +0000 Subject: [PATCH 05/16] Ignore Java reflection using tests in Scala.js --- tests/run/i12492/Test.scala | 1 + tests/run/i15318/Test.scala | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/run/i12492/Test.scala b/tests/run/i12492/Test.scala index 39313af4092b..6a06113e2501 100644 --- a/tests/run/i12492/Test.scala +++ b/tests/run/i12492/Test.scala @@ -1,3 +1,4 @@ +// scalajs: --skip object Test: def main(args: Array[String]): Unit = val cls = classOf[MyTable] diff --git a/tests/run/i15318/Test.scala b/tests/run/i15318/Test.scala index 192d22975261..06dfca076a38 100644 --- a/tests/run/i15318/Test.scala +++ b/tests/run/i15318/Test.scala @@ -1,3 +1,4 @@ +// scalajs: --skip object Test: def main(args: Array[String]): Unit = val cls = classOf[TestBeanProperty] From ad520f31c47736182b61ad59d051a64c61bc0062 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 3 Dec 2022 15:18:12 +0100 Subject: [PATCH 06/16] Use ||, not | --- compiler/src/dotty/tools/dotc/transform/BeanProperties.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index 4d2ab8da6533..5d0b55239b30 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -35,7 +35,7 @@ class BeanProperties(thisPhase: DenotTransformer): coord = annot.tree.span ).enteredAfter(thisPhase).asTerm val annots = valDef.symbol.annotations.filter { a => - a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) | !a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) + a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) || !a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) } meth.addAnnotations(annots) val body: Tree = ref(valDef.symbol) @@ -52,7 +52,7 @@ class BeanProperties(thisPhase: DenotTransformer): coord = annot.tree.span ).enteredAfter(thisPhase).asTerm val annots = valDef.symbol.annotations.filter { a => - a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) | !a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) + a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) || !a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) } meth.addAnnotations(annots) DefDef(meth, (params: List[List[Tree]]) => Assign(ref(valDef.symbol), params.head.head)).withSpan(meth.span) From c2c937e6476a34035a8921fb2c8f3aae2d994d7e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 10 Dec 2022 10:51:59 +0100 Subject: [PATCH 07/16] Make sure memoised accessors keep their meta annotation --- .../tools/dotc/transform/BeanProperties.scala | 3 ++- .../dotty/tools/dotc/transform/Memoize.scala | 4 ++-- tests/run/i12492.check | 20 +++++++++++-------- tests/run/i12492/MyTable.scala | 18 +++++++++++------ tests/run/i12492/Test.scala | 15 ++++++++------ tests/run/i15318.check | 6 ++---- tests/run/i15318/Test.scala | 15 ++++++++------ 7 files changed, 48 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index 5d0b55239b30..c8692d3c1d8e 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -55,7 +55,8 @@ class BeanProperties(thisPhase: DenotTransformer): a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) || !a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) } meth.addAnnotations(annots) - DefDef(meth, (params: List[List[Tree]]) => Assign(ref(valDef.symbol), params.head.head)).withSpan(meth.span) + def body(params: List[List[Tree]]): Tree = Assign(ref(valDef.symbol), params.head.head) + DefDef(meth, body).withSpan(meth.span) } def prefixedName(prefix: String, valName: Name) = diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 6456066bfdb0..5ae20223d9fa 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -4,7 +4,7 @@ package transform import core._ import DenotTransformers._ import Contexts._ -import Phases.phaseOf +import Phases.* import SymDenotations.SymDenotation import Denotations._ import Symbols._ @@ -130,7 +130,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => def removeUnwantedAnnotations(denot: SymDenotation, metaAnnotSym: ClassSymbol): Unit = if (sym.annotations.nonEmpty) { val cpy = sym.copySymDenotation() - cpy.filterAnnotations(_.symbol.hasAnnotation(metaAnnotSym)) + cpy.filterAnnotations(annot => atPhase(typerPhase)(annot.hasOneOfMetaAnnotation(metaAnnotSym))) cpy.installAfter(thisPhase) } diff --git a/tests/run/i12492.check b/tests/run/i12492.check index d251ced8beb1..6c39bdf5e71f 100644 --- a/tests/run/i12492.check +++ b/tests/run/i12492.check @@ -1,9 +1,13 @@ -inspecting field brandName -interface MyColumnBase -inspecting field companyName -interface MyColumnBase -inspecting method brandName -inspecting method companyName +inspecting field fieldName1 @MyColumnBase +inspecting field fieldName2 @MyColumnBase +inspecting field getterName1 @MyColumnBase +inspecting field getterName2 @MyColumnBase +inspecting method fieldName1 +inspecting method fieldName2 +inspecting method getterName1 @MyColumnBase +inspecting method getterName2 @MyColumnBase inspecting constructor MyTable -inspecting param brandName -inspecting param companyName +inspecting param fieldName1 +inspecting param fieldName2 +inspecting param getterName1 +inspecting param getterName2 diff --git a/tests/run/i12492/MyTable.scala b/tests/run/i12492/MyTable.scala index a6556c45b2b8..2cf34e7a9a49 100644 --- a/tests/run/i12492/MyTable.scala +++ b/tests/run/i12492/MyTable.scala @@ -1,10 +1,16 @@ -import scala.annotation.meta.field +import scala.annotation.meta.* -type MyColumn = MyColumnBase @field +type FieldColumn = MyColumnBase @field +type GetterColumn = MyColumnBase @getter class MyTable( - @(MyColumnBase @field)(name="BRAND_NAME") - val brandName: String, - @MyColumn(name="COMPANY_NAME") - val companyName: String, + @(MyColumnBase @field)(name="FIELD_NAME1") + val fieldName1: String, + @FieldColumn(name="FIELD_NAME2") + val fieldName2: String, + + @(MyColumnBase @getter)(name="GETTER_NAME1") + val getterName1: String, + @GetterColumn(name="GETTER_NAME2") + val getterName2: String, ) diff --git a/tests/run/i12492/Test.scala b/tests/run/i12492/Test.scala index 6a06113e2501..3a4a2c33754a 100644 --- a/tests/run/i12492/Test.scala +++ b/tests/run/i12492/Test.scala @@ -5,22 +5,25 @@ object Test: for (m <- cls.getDeclaredFields.sortBy(_.getName)) { m.setAccessible(true) - println(s"inspecting field ${m.getName}") + print(s"inspecting field ${m.getName}") for a <- m.getAnnotations().sortBy(_.annotationType.toString) do - println(a.annotationType) + print(s" @${a.annotationType.getName}") + println() } for (m <- cls.getDeclaredMethods.sortBy(_.getName)) { m.setAccessible(true) - println(s"inspecting method ${m.getName}") + print(s"inspecting method ${m.getName}") for a <- m.getAnnotations().sortBy(_.annotationType.toString) do - println(a.annotationType) + print(s" @${a.annotationType.getName}") + println() } for c <- cls.getDeclaredConstructors.sortBy(_.getName) do c.setAccessible(true) println(s"inspecting constructor ${c.getName}") for p <- c.getParameters.sortBy(_.getName) do - println(s"inspecting param ${p.getName}") + print(s"inspecting param ${p.getName}") for a <- p.getAnnotations.sortBy(_.annotationType.toString) do - println(a.annotationType) + print(s" @${a.annotationType.getName}") + println() diff --git a/tests/run/i15318.check b/tests/run/i15318.check index 8442a56a9d69..3bb559a7b5ce 100644 --- a/tests/run/i15318.check +++ b/tests/run/i15318.check @@ -1,7 +1,5 @@ -inspecting field value -interface JsonProperty -inspecting method getValue -interface JsonProperty +inspecting field value @JsonProperty +inspecting method getValue @JsonProperty inspecting method setValue inspecting method value inspecting method value_$eq diff --git a/tests/run/i15318/Test.scala b/tests/run/i15318/Test.scala index 06dfca076a38..d167cb033854 100644 --- a/tests/run/i15318/Test.scala +++ b/tests/run/i15318/Test.scala @@ -5,22 +5,25 @@ object Test: for (m <- cls.getDeclaredFields.sortBy(_.getName)) { m.setAccessible(true) - println(s"inspecting field ${m.getName}") + print(s"inspecting field ${m.getName}") for a <- m.getAnnotations().sortBy(_.annotationType.toString) do - println(a.annotationType) + print(s" @${a.annotationType.getName}") + println() } for (m <- cls.getDeclaredMethods.sortBy(_.getName)) { m.setAccessible(true) - println(s"inspecting method ${m.getName}") + print(s"inspecting method ${m.getName}") for a <- m.getAnnotations().sortBy(_.annotationType.toString) do - println(a.annotationType) + print(s" @${a.annotationType.getName}") + println() } for c <- cls.getDeclaredConstructors.sortBy(_.getName) do c.setAccessible(true) println(s"inspecting constructor ${c.getName}") for p <- c.getParameters.sortBy(_.getName) do - println(s"inspecting param ${p.getName}") + print(s"inspecting param ${p.getName}") for a <- p.getAnnotations.sortBy(_.annotationType.toString) do - println(a.annotationType) + print(s" @${a.annotationType.getName}") + println() From caf73bfb9f6ce3720347777c08cd0f7511cc6927 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 12 Dec 2022 10:29:36 +0000 Subject: [PATCH 08/16] Add an indirect `@beanGetter` example --- tests/run/i15318.check | 5 +++++ tests/run/i15318/TestBeanProperty.scala | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/tests/run/i15318.check b/tests/run/i15318.check index 3bb559a7b5ce..d5c309dac8fc 100644 --- a/tests/run/i15318.check +++ b/tests/run/i15318.check @@ -1,6 +1,11 @@ inspecting field value @JsonProperty +inspecting field value2 @JsonProperty inspecting method getValue @JsonProperty +inspecting method getValue2 @JsonProperty inspecting method setValue +inspecting method setValue2 inspecting method value +inspecting method value2 +inspecting method value2_$eq inspecting method value_$eq inspecting constructor TestBeanProperty diff --git a/tests/run/i15318/TestBeanProperty.scala b/tests/run/i15318/TestBeanProperty.scala index 3638b567ed7a..e36eaf783c5c 100644 --- a/tests/run/i15318/TestBeanProperty.scala +++ b/tests/run/i15318/TestBeanProperty.scala @@ -1,10 +1,16 @@ import scala.annotation.meta.beanGetter import scala.beans.BeanProperty +type BeanGetterJsonProperty = JsonProperty @beanGetter + class TestBeanProperty { @(JsonProperty @beanGetter)(value = "REAL_VALUE") @BeanProperty var value: String = _ + @BeanGetterJsonProperty(value = "REAL_VALUE2") + @BeanProperty + var value2: String = _ + } From fa6326ad56fb5c2c7e3d862af776d6b35688dfd7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 12 Dec 2022 17:00:31 +0000 Subject: [PATCH 09/16] Only retain `@field`-bearing annotations on fields --- compiler/src/dotty/tools/dotc/transform/Memoize.scala | 2 +- tests/run/i12492.check | 4 ++-- tests/run/i15318.check | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 5ae20223d9fa..8dfe056c2e15 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -122,7 +122,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => denot match { case fieldDenot: SymDenotation if sym.annotations.nonEmpty => val cpy = fieldDenot.copySymDenotation() - cpy.annotations = sym.annotations + cpy.annotations = atPhase(typerPhase)(sym.annotationsCarrying(defn.FieldMetaAnnot)) cpy.installAfter(thisPhase) case _ => () } diff --git a/tests/run/i12492.check b/tests/run/i12492.check index 6c39bdf5e71f..ebe50d6bccbf 100644 --- a/tests/run/i12492.check +++ b/tests/run/i12492.check @@ -1,7 +1,7 @@ inspecting field fieldName1 @MyColumnBase inspecting field fieldName2 @MyColumnBase -inspecting field getterName1 @MyColumnBase -inspecting field getterName2 @MyColumnBase +inspecting field getterName1 +inspecting field getterName2 inspecting method fieldName1 inspecting method fieldName2 inspecting method getterName1 @MyColumnBase diff --git a/tests/run/i15318.check b/tests/run/i15318.check index d5c309dac8fc..c0b2350767b3 100644 --- a/tests/run/i15318.check +++ b/tests/run/i15318.check @@ -1,5 +1,5 @@ -inspecting field value @JsonProperty -inspecting field value2 @JsonProperty +inspecting field value +inspecting field value2 inspecting method getValue @JsonProperty inspecting method getValue2 @JsonProperty inspecting method setValue From e580aaaef8df2b162817050d2eb0f17483423359 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 13 Dec 2022 13:41:38 +0000 Subject: [PATCH 10/16] Correctly shuffle annotations to where they should go --- .../dotty/tools/dotc/core/Definitions.scala | 2 + .../tools/dotc/transform/BeanProperties.scala | 8 ++-- .../dotty/tools/dotc/transform/Memoize.scala | 5 +- tests/run/i12492.check | 48 ++++++++++++++----- tests/run/i12492/MyColumnBase.java | 4 +- tests/run/i12492/MyTable.scala | 35 +++++++++----- tests/run/i12492/Test.scala | 22 +++++---- tests/run/i15318.check | 41 +++++++++++----- tests/run/i15318/Bean.scala | 21 ++++++++ tests/run/i15318/JsonProperty.java | 4 +- tests/run/i15318/Test.scala | 22 +++++---- tests/run/i15318/TestBeanProperty.scala | 16 ------- 12 files changed, 146 insertions(+), 82 deletions(-) create mode 100644 tests/run/i15318/Bean.scala delete mode 100644 tests/run/i15318/TestBeanProperty.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 72d7749bb495..a3b4ac124768 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1041,6 +1041,8 @@ class Definitions { // A list of meta-annotations that are relevant for fields and accessors @tu lazy val FieldAccessorMetaAnnots: Set[Symbol] = Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot) + @tu lazy val ExtFieldAccessorMetaAnnots: Set[Symbol] = + FieldAccessorMetaAnnots + BeanGetterMetaAnnot + BeanSetterMetaAnnot // A list of annotations that are commonly used to indicate that a field/method argument or return // type is not null. These annotations are used by the nullification logic in JavaNullInterop to diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index c8692d3c1d8e..076700e9afd8 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -34,8 +34,8 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(Nil, valDef.denot.info), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - val annots = valDef.symbol.annotations.filter { a => - a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) || !a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) + val annots = valDef.symbol.annotations.filterConserve { a => + a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) } meth.addAnnotations(annots) val body: Tree = ref(valDef.symbol) @@ -51,8 +51,8 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(valDef.name :: Nil, valDef.denot.info :: Nil, defn.UnitType), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - val annots = valDef.symbol.annotations.filter { a => - a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) || !a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) + val annots = valDef.symbol.annotations.filterConserve { a => + a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) } meth.addAnnotations(annots) def body(params: List[List[Tree]]): Tree = Assign(ref(valDef.symbol), params.head.head) diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 8dfe056c2e15..3c1183505043 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -122,7 +122,10 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => denot match { case fieldDenot: SymDenotation if sym.annotations.nonEmpty => val cpy = fieldDenot.copySymDenotation() - cpy.annotations = atPhase(typerPhase)(sym.annotationsCarrying(defn.FieldMetaAnnot)) + cpy.annotations = atPhase(typerPhase)(sym.annotations.filterConserve { annot => + annot.hasOneOfMetaAnnotation(defn.FieldMetaAnnot) + || !annot.hasOneOfMetaAnnotation(defn.ExtFieldAccessorMetaAnnots.toList*) + }) cpy.installAfter(thisPhase) case _ => () } diff --git a/tests/run/i12492.check b/tests/run/i12492.check index ebe50d6bccbf..1c38f98a3c09 100644 --- a/tests/run/i12492.check +++ b/tests/run/i12492.check @@ -1,13 +1,37 @@ -inspecting field fieldName1 @MyColumnBase -inspecting field fieldName2 @MyColumnBase -inspecting field getterName1 -inspecting field getterName2 -inspecting method fieldName1 -inspecting method fieldName2 -inspecting method getterName1 @MyColumnBase -inspecting method getterName2 @MyColumnBase inspecting constructor MyTable -inspecting param fieldName1 -inspecting param fieldName2 -inspecting param getterName1 -inspecting param getterName2 +inspecting param aaaParam1 @MyColumnBase +inspecting param fldParam1 +inspecting param getParam1 +inspecting param parParam1 @MyColumnBase +inspecting field aaaField1 @MyColumnBase +inspecting field aaaParam1 +inspecting field fldField1 @MyColumnBase +inspecting field fldParam1 @MyColumnBase +inspecting field getField1 +inspecting field getParam1 +inspecting field parField1 +inspecting field parParam1 +inspecting method aaaField1 +inspecting method aaaParam1 +inspecting method fldField1 +inspecting method fldParam1 +inspecting method getField1 @MyColumnBase +inspecting method getParam1 @MyColumnBase +inspecting method parField1 +inspecting method parParam1 +inspecting constructor MyTable2 +inspecting param fldParam2 +inspecting param getParam2 +inspecting param parParam2 @MyColumnBase +inspecting field fldField2 @MyColumnBase +inspecting field fldParam2 @MyColumnBase +inspecting field getField2 +inspecting field getParam2 +inspecting field parField2 +inspecting field parParam2 +inspecting method fldField2 +inspecting method fldParam2 +inspecting method getField2 @MyColumnBase +inspecting method getParam2 @MyColumnBase +inspecting method parField2 +inspecting method parParam2 diff --git a/tests/run/i12492/MyColumnBase.java b/tests/run/i12492/MyColumnBase.java index e0253649a1f3..f24b50b8d80c 100644 --- a/tests/run/i12492/MyColumnBase.java +++ b/tests/run/i12492/MyColumnBase.java @@ -2,6 +2,4 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) -public @interface MyColumnBase { - String name() default ""; -} +public @interface MyColumnBase {} diff --git a/tests/run/i12492/MyTable.scala b/tests/run/i12492/MyTable.scala index 2cf34e7a9a49..b22f76c5ec91 100644 --- a/tests/run/i12492/MyTable.scala +++ b/tests/run/i12492/MyTable.scala @@ -1,16 +1,27 @@ -import scala.annotation.meta.* +import scala.annotation.meta.{ field as fld, getter as get, param as par } -type FieldColumn = MyColumnBase @field -type GetterColumn = MyColumnBase @getter +type FldColumn = MyColumnBase @fld +type GetColumn = MyColumnBase @get +type ParColumn = MyColumnBase @par class MyTable( - @(MyColumnBase @field)(name="FIELD_NAME1") - val fieldName1: String, - @FieldColumn(name="FIELD_NAME2") - val fieldName2: String, + @(MyColumnBase ) val aaaParam1: String, + @(MyColumnBase @fld) val fldParam1: String, + @(MyColumnBase @get) val getParam1: String, + @(MyColumnBase @par) val parParam1: String, +) { + @(MyColumnBase ) val aaaField1: String = "" + @(MyColumnBase @fld) val fldField1: String = "" + @(MyColumnBase @get) val getField1: String = "" + @(MyColumnBase @par) val parField1: String = "" +} - @(MyColumnBase @getter)(name="GETTER_NAME1") - val getterName1: String, - @GetterColumn(name="GETTER_NAME2") - val getterName2: String, -) +class MyTable2( + @FldColumn val fldParam2: String, + @GetColumn val getParam2: String, + @ParColumn val parParam2: String, +) { + @FldColumn val fldField2: String = "" + @GetColumn val getField2: String = "" + @ParColumn val parField2: String = "" +} diff --git a/tests/run/i12492/Test.scala b/tests/run/i12492/Test.scala index 3a4a2c33754a..190b84fd66f5 100644 --- a/tests/run/i12492/Test.scala +++ b/tests/run/i12492/Test.scala @@ -1,7 +1,18 @@ // scalajs: --skip object Test: def main(args: Array[String]): Unit = - val cls = classOf[MyTable] + go(classOf[MyTable]) + go(classOf[MyTable2]) + + def go(cls: Class[?]): Unit = + for c <- cls.getDeclaredConstructors.sortBy(_.getName) do + c.setAccessible(true) + println(s"inspecting constructor ${c.getName}") + for p <- c.getParameters.sortBy(_.getName) do + print(s"inspecting param ${p.getName}") + for a <- p.getAnnotations.sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() for (m <- cls.getDeclaredFields.sortBy(_.getName)) { m.setAccessible(true) @@ -18,12 +29,3 @@ object Test: print(s" @${a.annotationType.getName}") println() } - - for c <- cls.getDeclaredConstructors.sortBy(_.getName) do - c.setAccessible(true) - println(s"inspecting constructor ${c.getName}") - for p <- c.getParameters.sortBy(_.getName) do - print(s"inspecting param ${p.getName}") - for a <- p.getAnnotations.sortBy(_.annotationType.toString) do - print(s" @${a.annotationType.getName}") - println() diff --git a/tests/run/i15318.check b/tests/run/i15318.check index c0b2350767b3..d1fb39e729e8 100644 --- a/tests/run/i15318.check +++ b/tests/run/i15318.check @@ -1,11 +1,30 @@ -inspecting field value -inspecting field value2 -inspecting method getValue @JsonProperty -inspecting method getValue2 @JsonProperty -inspecting method setValue -inspecting method setValue2 -inspecting method value -inspecting method value2 -inspecting method value2_$eq -inspecting method value_$eq -inspecting constructor TestBeanProperty +inspecting constructor Bean +inspecting field beanAaa @JsonProperty +inspecting field beanGet +inspecting field beanSet +inspecting field normAaa @JsonProperty +inspecting field normFld @JsonProperty +inspecting field normGet +inspecting field normSet +inspecting method beanAaa +inspecting method beanGet +inspecting method beanSet +inspecting method beanSet_$eq +inspecting method getBeanAaa +inspecting method getBeanGet @JsonProperty +inspecting method getBeanSet +inspecting method normAaa +inspecting method normFld +inspecting method normGet @JsonProperty +inspecting method normSet +inspecting method normSet_$eq @JsonProperty +inspecting method setBeanSet @JsonProperty +inspecting constructor Bean2 +inspecting field beanGet +inspecting field beanSet +inspecting method beanGet +inspecting method beanSet +inspecting method beanSet_$eq +inspecting method getBeanGet @JsonProperty +inspecting method getBeanSet +inspecting method setBeanSet @JsonProperty diff --git a/tests/run/i15318/Bean.scala b/tests/run/i15318/Bean.scala new file mode 100644 index 000000000000..0833a264806f --- /dev/null +++ b/tests/run/i15318/Bean.scala @@ -0,0 +1,21 @@ +import scala.annotation.meta.{ field as fld, getter as get, setter as set, * } +import scala.beans.BeanProperty + +type BeanGetJsonProperty = JsonProperty @beanGetter +type BeanSetJsonProperty = JsonProperty @beanSetter + +class Bean { + @(JsonProperty ) val normAaa: String = "" + @(JsonProperty @fld) val normFld: String = "" + @(JsonProperty @get) val normGet: String = "" + @(JsonProperty @set) var normSet: String = "" + + @(JsonProperty ) @BeanProperty val beanAaa: String = "" + @(JsonProperty @beanGetter) @BeanProperty val beanGet: String = "" + @(JsonProperty @beanSetter) @BeanProperty var beanSet: String = "" +} + +class Bean2 { + @BeanGetJsonProperty @BeanProperty val beanGet: String = "" + @BeanSetJsonProperty @BeanProperty var beanSet: String = "" +} diff --git a/tests/run/i15318/JsonProperty.java b/tests/run/i15318/JsonProperty.java index 0d96489aeaf0..0f588b63dc7d 100644 --- a/tests/run/i15318/JsonProperty.java +++ b/tests/run/i15318/JsonProperty.java @@ -2,6 +2,4 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) -public @interface JsonProperty { - String value() default ""; -} +public @interface JsonProperty {} diff --git a/tests/run/i15318/Test.scala b/tests/run/i15318/Test.scala index d167cb033854..1669a3f25e1d 100644 --- a/tests/run/i15318/Test.scala +++ b/tests/run/i15318/Test.scala @@ -1,7 +1,18 @@ // scalajs: --skip object Test: def main(args: Array[String]): Unit = - val cls = classOf[TestBeanProperty] + go(classOf[Bean]) + go(classOf[Bean2]) + + def go(cls: Class[?]): Unit = + for c <- cls.getDeclaredConstructors.sortBy(_.getName) do + c.setAccessible(true) + println(s"inspecting constructor ${c.getName}") + for p <- c.getParameters.sortBy(_.getName) do + print(s"inspecting param ${p.getName}") + for a <- p.getAnnotations.sortBy(_.annotationType.toString) do + print(s" @${a.annotationType.getName}") + println() for (m <- cls.getDeclaredFields.sortBy(_.getName)) { m.setAccessible(true) @@ -18,12 +29,3 @@ object Test: print(s" @${a.annotationType.getName}") println() } - - for c <- cls.getDeclaredConstructors.sortBy(_.getName) do - c.setAccessible(true) - println(s"inspecting constructor ${c.getName}") - for p <- c.getParameters.sortBy(_.getName) do - print(s"inspecting param ${p.getName}") - for a <- p.getAnnotations.sortBy(_.annotationType.toString) do - print(s" @${a.annotationType.getName}") - println() diff --git a/tests/run/i15318/TestBeanProperty.scala b/tests/run/i15318/TestBeanProperty.scala deleted file mode 100644 index e36eaf783c5c..000000000000 --- a/tests/run/i15318/TestBeanProperty.scala +++ /dev/null @@ -1,16 +0,0 @@ -import scala.annotation.meta.beanGetter -import scala.beans.BeanProperty - -type BeanGetterJsonProperty = JsonProperty @beanGetter - -class TestBeanProperty { - - @(JsonProperty @beanGetter)(value = "REAL_VALUE") - @BeanProperty - var value: String = _ - - @BeanGetterJsonProperty(value = "REAL_VALUE2") - @BeanProperty - var value2: String = _ - -} From f847ef60d82adb3b082eb36f4f36e8ffe78d5727 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 13 Dec 2022 16:18:15 +0000 Subject: [PATCH 11/16] Switch to atPhaseNoLater & push it upstream --- compiler/src/dotty/tools/dotc/core/Annotations.scala | 10 ++++++---- compiler/src/dotty/tools/dotc/transform/Memoize.scala | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 51f791456eb1..d349d6882e93 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -2,12 +2,13 @@ package dotty.tools package dotc package core -import Symbols._, Types._, Contexts._, Constants._ -import dotty.tools.dotc.ast.tpd, tpd.* +import Symbols._, Types._, Contexts._, Constants._, Phases.* +import ast.tpd, tpd.* import util.Spans.Span import printing.{Showable, Printer} import printing.Texts.Text -import annotation.internal.sharable + +import scala.annotation.internal.sharable object Annotations { @@ -87,7 +88,7 @@ object Annotations { def sameAnnotation(that: Annotation)(using Context): Boolean = symbol == that.symbol && tree.sameTree(that.tree) - def hasOneOfMetaAnnotation(metaSyms: Symbol*)(using Context): Boolean = + def hasOneOfMetaAnnotation(metaSyms: Symbol*)(using Context): Boolean = atPhaseNoLater(erasurePhase) { def recTp(tp: Type): Boolean = tp.dealiasKeepAnnots match case AnnotatedType(parent, metaAnnot) => metaSyms.exists(metaAnnot.matches) || recTp(parent) case _ => false @@ -99,6 +100,7 @@ object Annotations { case Typed(expr, _) => rec(expr) case _ => false metaSyms.exists(symbol.hasAnnotation) || rec(tree) + } /** Operations for hash-consing, can be overridden */ def hash: Int = System.identityHashCode(this) diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 3c1183505043..8b0fc7a8430e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -133,7 +133,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => def removeUnwantedAnnotations(denot: SymDenotation, metaAnnotSym: ClassSymbol): Unit = if (sym.annotations.nonEmpty) { val cpy = sym.copySymDenotation() - cpy.filterAnnotations(annot => atPhase(typerPhase)(annot.hasOneOfMetaAnnotation(metaAnnotSym))) + cpy.filterAnnotations(annot => annot.hasOneOfMetaAnnotation(metaAnnotSym)) cpy.installAfter(thisPhase) } From ceabdbd176281dee50a91975c6c09b1a6bac288b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 13 Dec 2022 16:18:39 +0000 Subject: [PATCH 12/16] Set field annotations immediately --- .../dotty/tools/dotc/transform/Memoize.scala | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 8b0fc7a8430e..b0e40033b23f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -108,27 +108,19 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => if (sym.isGetter) sym.info.resultType else /*sym.isSetter*/ sym.info.firstParamTypes.head - newSymbol( + val fieldSym = newSymbol( owner = ctx.owner, name = sym.name.asTermName.fieldName, flags = Private | (if (sym.is(StableRealizable)) EmptyFlags else Mutable), info = fieldType, coord = tree.span - ).withAnnotationsCarrying(sym, defn.FieldMetaAnnot) - .enteredAfter(thisPhase) - } - - def addAnnotations(denot: Denotation): Unit = - denot match { - case fieldDenot: SymDenotation if sym.annotations.nonEmpty => - val cpy = fieldDenot.copySymDenotation() - cpy.annotations = atPhase(typerPhase)(sym.annotations.filterConserve { annot => - annot.hasOneOfMetaAnnotation(defn.FieldMetaAnnot) - || !annot.hasOneOfMetaAnnotation(defn.ExtFieldAccessorMetaAnnots.toList*) - }) - cpy.installAfter(thisPhase) - case _ => () + ) + fieldSym.annotations = sym.annotations.filterConserve { annot => + annot.hasOneOfMetaAnnotation(defn.FieldMetaAnnot) + || !annot.hasOneOfMetaAnnotation(defn.ExtFieldAccessorMetaAnnots.toList*) } + fieldSym.enteredAfter(thisPhase) + } def removeUnwantedAnnotations(denot: SymDenotation, metaAnnotSym: ClassSymbol): Unit = if (sym.annotations.nonEmpty) { @@ -186,7 +178,6 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass) else transformFollowingDeep(ref(field))(using ctx.withOwner(sym)) val getterDef = cpy.DefDef(tree)(rhs = getterRhs) - addAnnotations(fieldDef.denot) removeUnwantedAnnotations(sym, defn.GetterMetaAnnot) Thicket(fieldDef, getterDef) else if sym.isSetter then From 8c512cd156bed37863f8ea9cd45223dc84b5111c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 13 Dec 2022 16:18:55 +0000 Subject: [PATCH 13/16] Fix tests/run/beans expectations --- tests/run/beans.check | 1 + tests/run/beans/Test_3.java | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/run/beans.check b/tests/run/beans.check index eb70eefb720c..3a6d7b04e6a8 100644 --- a/tests/run/beans.check +++ b/tests/run/beans.check @@ -2,5 +2,6 @@ true 10 [@beans.LibraryAnnotation_1()] +[] some text other text diff --git a/tests/run/beans/Test_3.java b/tests/run/beans/Test_3.java index 4ac967419e50..ad515d787a0e 100644 --- a/tests/run/beans/Test_3.java +++ b/tests/run/beans/Test_3.java @@ -7,6 +7,7 @@ public A run() throws ReflectiveOperationException{ System.out.println(a.isY()); System.out.println(new T2().getX()); + System.out.println(Arrays.asList(a.getClass().getDeclaredField("retainingAnnotation").getAnnotations())); System.out.println(Arrays.asList(a.getClass().getMethod("getRetainingAnnotation").getAnnotations())); System.out.println(a.getMutableOneWithLongName()); From e35dc2a3e68088f6f49c60f0c5914903e9884e3c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 13 Dec 2022 16:22:45 +0000 Subject: [PATCH 14/16] Rename meta annotation sets --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 6 +++--- compiler/src/dotty/tools/dotc/transform/Memoize.scala | 2 +- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index a3b4ac124768..360132eaeb8f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1039,10 +1039,10 @@ class Definitions { @tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable") // A list of meta-annotations that are relevant for fields and accessors - @tu lazy val FieldAccessorMetaAnnots: Set[Symbol] = + @tu lazy val NonBeanMetaAnnots: Set[Symbol] = Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot) - @tu lazy val ExtFieldAccessorMetaAnnots: Set[Symbol] = - FieldAccessorMetaAnnots + BeanGetterMetaAnnot + BeanSetterMetaAnnot + @tu lazy val MetaAnnots: Set[Symbol] = + NonBeanMetaAnnots + BeanGetterMetaAnnot + BeanSetterMetaAnnot // A list of annotations that are commonly used to indicate that a field/method argument or return // type is not null. These annotations are used by the nullification logic in JavaNullInterop to diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index b0e40033b23f..62b7b31ce73c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -117,7 +117,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => ) fieldSym.annotations = sym.annotations.filterConserve { annot => annot.hasOneOfMetaAnnotation(defn.FieldMetaAnnot) - || !annot.hasOneOfMetaAnnotation(defn.ExtFieldAccessorMetaAnnots.toList*) + || !annot.hasOneOfMetaAnnotation(defn.MetaAnnots.toList*) } fieldSym.enteredAfter(thisPhase) } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index ad75b79be8ae..6b6473658386 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -188,7 +188,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase metaAnnotSymBackup: Symbol, keepIfNoRelevantAnnot: Boolean)(using Context): Unit = def shouldKeep(annot: Annotation): Boolean = annot.hasOneOfMetaAnnotation(metaAnnotSym, metaAnnotSymBackup) - || keepIfNoRelevantAnnot && !annot.hasOneOfMetaAnnotation(defn.FieldAccessorMetaAnnots.toList*) + || keepIfNoRelevantAnnot && !annot.hasOneOfMetaAnnotation(defn.NonBeanMetaAnnots.toList*) if sym.annotations.nonEmpty then sym.filterAnnotations(shouldKeep(_)) From 0bd5087b2123eb766fb7cce73deaa9b77337d011 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 13 Dec 2022 16:58:36 +0000 Subject: [PATCH 15/16] Reuse SymUtils.withAnnotationsCarrying --- .../dotty/tools/dotc/transform/BeanProperties.scala | 11 +++-------- compiler/src/dotty/tools/dotc/transform/Memoize.scala | 2 +- .../src/dotty/tools/dotc/transform/SymUtils.scala | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index 076700e9afd8..2a7429436cc2 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -6,6 +6,7 @@ import ast.tpd._ import Annotations._ import Contexts._ import Symbols.newSymbol +import SymUtils.* import Decorators._ import Flags._ import Names._ @@ -34,10 +35,7 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(Nil, valDef.denot.info), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - val annots = valDef.symbol.annotations.filterConserve { a => - a.hasOneOfMetaAnnotation(defn.BeanGetterMetaAnnot) - } - meth.addAnnotations(annots) + .withAnnotationsCarrying(valDef.symbol, defn.BeanGetterMetaAnnot) val body: Tree = ref(valDef.symbol) DefDef(meth, body).withSpan(meth.span) @@ -51,10 +49,7 @@ class BeanProperties(thisPhase: DenotTransformer): info = MethodType(valDef.name :: Nil, valDef.denot.info :: Nil, defn.UnitType), coord = annot.tree.span ).enteredAfter(thisPhase).asTerm - val annots = valDef.symbol.annotations.filterConserve { a => - a.hasOneOfMetaAnnotation(defn.BeanSetterMetaAnnot) - } - meth.addAnnotations(annots) + .withAnnotationsCarrying(valDef.symbol, defn.BeanSetterMetaAnnot) def body(params: List[List[Tree]]): Tree = Assign(ref(valDef.symbol), params.head.head) DefDef(meth, body).withSpan(meth.span) } diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 62b7b31ce73c..e65fded05592 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -125,7 +125,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => def removeUnwantedAnnotations(denot: SymDenotation, metaAnnotSym: ClassSymbol): Unit = if (sym.annotations.nonEmpty) { val cpy = sym.copySymDenotation() - cpy.filterAnnotations(annot => annot.hasOneOfMetaAnnotation(metaAnnotSym)) + cpy.filterAnnotations(_.hasOneOfMetaAnnotation(metaAnnotSym)) cpy.installAfter(thisPhase) } diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index d6312f0c07d6..f84c7f1efeb0 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -271,7 +271,7 @@ object SymUtils: self.isAllOf(EnumCase, butNot = JavaDefined) def annotationsCarrying(meta: ClassSymbol)(using Context): List[Annotation] = - self.annotations.filter(_.hasOneOfMetaAnnotation(meta)) + self.annotations.filterConserve(_.hasOneOfMetaAnnotation(meta)) def withAnnotationsCarrying(from: Symbol, meta: ClassSymbol)(using Context): self.type = { self.addAnnotations(from.annotationsCarrying(meta)) From ff68dc3ec802d9f6fff488295419157ee3bbc052 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 13 Dec 2022 23:31:12 +0000 Subject: [PATCH 16/16] Push copyAndKeepAnnotationsCarrying into SymDenotation --- .../dotty/tools/dotc/core/Annotations.scala | 26 ++++++++++--------- .../tools/dotc/core/SymDenotations.scala | 9 +++++++ .../tools/dotc/transform/BeanProperties.scala | 4 +-- .../dotty/tools/dotc/transform/Memoize.scala | 21 ++++----------- .../tools/dotc/transform/PostTyper.scala | 15 ++++------- .../dotty/tools/dotc/transform/SymUtils.scala | 7 ++--- 6 files changed, 36 insertions(+), 46 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index d349d6882e93..532ec936f7ec 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -88,18 +88,20 @@ object Annotations { def sameAnnotation(that: Annotation)(using Context): Boolean = symbol == that.symbol && tree.sameTree(that.tree) - def hasOneOfMetaAnnotation(metaSyms: Symbol*)(using Context): Boolean = atPhaseNoLater(erasurePhase) { - def recTp(tp: Type): Boolean = tp.dealiasKeepAnnots match - case AnnotatedType(parent, metaAnnot) => metaSyms.exists(metaAnnot.matches) || recTp(parent) - case _ => false - def rec(tree: Tree): Boolean = methPart(tree) match - case New(tpt) => rec(tpt) - case Select(qual, _) => rec(qual) - case Annotated(arg, metaAnnot) => metaSyms.exists(metaAnnot.tpe.classSymbol.derivesFrom) || rec(arg) - case t @ Ident(_) => recTp(t.tpe) - case Typed(expr, _) => rec(expr) - case _ => false - metaSyms.exists(symbol.hasAnnotation) || rec(tree) + def hasOneOfMetaAnnotation(metaSyms: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Boolean = atPhaseNoLater(erasurePhase) { + def go(metaSyms: Set[Symbol]) = + def recTp(tp: Type): Boolean = tp.dealiasKeepAnnots match + case AnnotatedType(parent, metaAnnot) => metaSyms.exists(metaAnnot.matches) || recTp(parent) + case _ => false + def rec(tree: Tree): Boolean = methPart(tree) match + case New(tpt) => rec(tpt) + case Select(qual, _) => rec(qual) + case Annotated(arg, metaAnnot) => metaSyms.exists(metaAnnot.tpe.classSymbol.derivesFrom) || rec(arg) + case t @ Ident(_) => recTp(t.tpe) + case Typed(expr, _) => rec(expr) + case _ => false + metaSyms.exists(symbol.hasAnnotation) || rec(tree) + go(metaSyms) || orNoneOf.nonEmpty && !go(orNoneOf) } /** Operations for hash-consing, can be overridden */ diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 134781f7de19..414ab3fa3c14 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -252,6 +252,15 @@ object SymDenotations { final def filterAnnotations(p: Annotation => Boolean)(using Context): Unit = annotations = annotations.filterConserve(p) + def annotationsCarrying(meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): List[Annotation] = + annotations.filterConserve(_.hasOneOfMetaAnnotation(meta, orNoneOf = orNoneOf)) + + def copyAndKeepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit = + if annotations.nonEmpty then + val cpy = copySymDenotation() + cpy.annotations = annotationsCarrying(meta, orNoneOf = orNoneOf) + cpy.installAfter(phase) + /** Optionally, the annotation matching the given class symbol */ final def getAnnotation(cls: Symbol)(using Context): Option[Annotation] = dropOtherAnnotations(annotations, cls) match { diff --git a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala index 2a7429436cc2..0c1f40d4f2bd 100644 --- a/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala +++ b/compiler/src/dotty/tools/dotc/transform/BeanProperties.scala @@ -5,7 +5,7 @@ import core._ import ast.tpd._ import Annotations._ import Contexts._ -import Symbols.newSymbol +import Symbols.* import SymUtils.* import Decorators._ import Flags._ @@ -24,8 +24,6 @@ class BeanProperties(thisPhase: DenotTransformer): } ::: origBody) def generateAccessors(valDef: ValDef)(using Context): List[Tree] = - import Symbols.defn - def generateGetter(valDef: ValDef, annot: Annotation)(using Context) : Tree = val prefix = if annot matches defn.BooleanBeanPropertyAnnot then "is" else "get" val meth = newSymbol( diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index e65fded05592..5a2eda4101a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -108,27 +108,16 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => if (sym.isGetter) sym.info.resultType else /*sym.isSetter*/ sym.info.firstParamTypes.head - val fieldSym = newSymbol( + newSymbol( owner = ctx.owner, name = sym.name.asTermName.fieldName, flags = Private | (if (sym.is(StableRealizable)) EmptyFlags else Mutable), info = fieldType, coord = tree.span - ) - fieldSym.annotations = sym.annotations.filterConserve { annot => - annot.hasOneOfMetaAnnotation(defn.FieldMetaAnnot) - || !annot.hasOneOfMetaAnnotation(defn.MetaAnnots.toList*) - } - fieldSym.enteredAfter(thisPhase) + ).withAnnotationsCarrying(sym, defn.FieldMetaAnnot, orNoneOf = defn.MetaAnnots) + .enteredAfter(thisPhase) } - def removeUnwantedAnnotations(denot: SymDenotation, metaAnnotSym: ClassSymbol): Unit = - if (sym.annotations.nonEmpty) { - val cpy = sym.copySymDenotation() - cpy.filterAnnotations(_.hasOneOfMetaAnnotation(metaAnnotSym)) - cpy.installAfter(thisPhase) - } - val NoFieldNeeded = Lazy | Deferred | JavaDefined | Inline def erasedBottomTree(sym: Symbol) = @@ -178,7 +167,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass) else transformFollowingDeep(ref(field))(using ctx.withOwner(sym)) val getterDef = cpy.DefDef(tree)(rhs = getterRhs) - removeUnwantedAnnotations(sym, defn.GetterMetaAnnot) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot)) Thicket(fieldDef, getterDef) else if sym.isSetter then if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs: @unchecked } // This is intended as an assertion @@ -204,7 +193,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => then Literal(Constant(())) else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol))) val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym))) - removeUnwantedAnnotations(sym, defn.SetterMetaAnnot) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) setterDef else // Curiously, some accessors from Scala2 have ' ' suffixes. diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 6b6473658386..eec7a1c85b3b 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -157,12 +157,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase checkInferredWellFormed(tree.tpt) if sym.is(Method) then if sym.isSetter then - removeUnwantedAnnotations(sym, defn.SetterMetaAnnot, NoSymbol, keepIfNoRelevantAnnot = false) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) else if sym.is(Param) then - removeUnwantedAnnotations(sym, defn.ParamMetaAnnot, NoSymbol, keepIfNoRelevantAnnot = true) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) + else if sym.is(ParamAccessor) then + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot)) else - removeUnwantedAnnotations(sym, defn.GetterMetaAnnot, defn.FieldMetaAnnot, keepIfNoRelevantAnnot = !sym.is(ParamAccessor)) + sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature) // Allow scala.reflect.materializeClassTag to be able to compile scala/reflect/package.scala @@ -184,13 +186,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase => Checking.checkAppliedTypesIn(tree) case _ => - private def removeUnwantedAnnotations(sym: Symbol, metaAnnotSym: Symbol, - metaAnnotSymBackup: Symbol, keepIfNoRelevantAnnot: Boolean)(using Context): Unit = - def shouldKeep(annot: Annotation): Boolean = - annot.hasOneOfMetaAnnotation(metaAnnotSym, metaAnnotSymBackup) - || keepIfNoRelevantAnnot && !annot.hasOneOfMetaAnnotation(defn.NonBeanMetaAnnots.toList*) - if sym.annotations.nonEmpty then - sym.filterAnnotations(shouldKeep(_)) private def transformSelect(tree: Select, targs: List[Tree])(using Context): Tree = { val qual = tree.qualifier diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index f84c7f1efeb0..766f2ab23873 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -270,11 +270,8 @@ object SymUtils: def isEnumCase(using Context): Boolean = self.isAllOf(EnumCase, butNot = JavaDefined) - def annotationsCarrying(meta: ClassSymbol)(using Context): List[Annotation] = - self.annotations.filterConserve(_.hasOneOfMetaAnnotation(meta)) - - def withAnnotationsCarrying(from: Symbol, meta: ClassSymbol)(using Context): self.type = { - self.addAnnotations(from.annotationsCarrying(meta)) + def withAnnotationsCarrying(from: Symbol, meta: Symbol, orNoneOf: Set[Symbol] = Set.empty)(using Context): self.type = { + self.addAnnotations(from.annotationsCarrying(Set(meta), orNoneOf)) self }