Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AnnotationInfo when using named/default arguments, support custom annotation subclasses #10976

Open
wants to merge 6 commits into
base: 2.13.x
Choose a base branch
from

Conversation

lrytz
Copy link
Member

@lrytz lrytz commented Jan 7, 2025

In an ordinary application f(y = b, x = a), the compiler needs to create a block with local variables

{
  val x$1 = b
  val x$2 = a
  f(x$2, x$1)
}

in order to preserve evaluation order (b before a).

For annotations @ann(y = b, x = a) this is not needed, and it makes annotation processing more difficult / impossible.

This PR disables the named / default arguments transformation for annotations.

Furthermore, when an annotation uses a default argument, the compiler inserts a copy of the default expression AST to the AnnotationInfo (instaead of a call to the annot$init$default$1 method):

scala> :power
scala> class ann(x: Int = 1, y: Int = 2) extends annotation.Annotation
scala> @ann(y = 22) class C
scala> typeOf[C].typeSymbol.annotations
  List(ann(1, 22))

The default expression ASTs are stored in meta-annotations so that they can be recovered later when using an annotation:

scala> typeOf[ann].typeSymbol.primaryConstructor.paramss.flatten.map(p => (p, p.annotations))
  List((value x,List(scala.annotation.meta.defaultArg(1))), (value y,List(scala.annotation.meta.defaultArg(2))))

This PR also adds support for user-defined annotation subclasses. The annotationInfo.argsForSuper method returns the corresponding annotation arguments:

scala> class sub(z: Int) extends ann(11, z)
scala> @sub(z = 33) class D

scala> val info = typeOf[D].typeSymbol.annotations.head
val info: $r.intp.global.AnnotationInfo = sub(33)

scala> info.argsForSuper(typeOf[ann].typeSymbol)
val res5: List[$r.intp.global.Tree] = List(11, 33)

Arguments passed to the super annotation are encoded in yet more meta annotations:

scala> typeOf[sub].typeSymbol.annotations
val res6: List[$r.intp.global.AnnotationInfo] = List(scala.annotation.meta.superArg("x", 11), scala.annotation.meta.superFwdArg(p = "y", n = "z"))

Finally, this PR adds support for custom @nowarn subclasses

scala> class nodep extends annotation.nowarn("cat=deprecation")
scala> @nodep def f = Map().mapValues(identity)
def f: scala.collection.MapView[Nothing,Nothing]

Fixes scala/scala-dev#884, scala/bug#7656, scala/bug#9612, scala/bug#12367

@scala-jenkins scala-jenkins added this to the 2.13.17 milestone Jan 7, 2025
@lrytz lrytz force-pushed the sd884 branch 6 times, most recently from 8676101 to 55255a8 Compare January 10, 2025 08:10
@lrytz
Copy link
Member Author

lrytz commented Jan 10, 2025

Added support for default arguments.

The behavior differes betwen Java/ConstantAnnotation and other annotations.

In 2.13.15:

  • missing arguments for a Java/ConstantAnnotation are simply not present in annotationInfo.assocs
  • missing args for other annotations are present in args as annot.init$default$1 method call ASTs

In order to remain mostly compatible, in this PR:

  • assocs still returns only explicit arguments, the new annotationInfo.assocsWithDefaults method extends assocs with the default values
  • default arguments in args are now an AST corresponding to the default expression. The new annotationInfo.argIsDefault(arg) method tells whether an whether an argument was explicitly given or a default inserted by the compiler. Internally this is represented by tagging the arg.tpe with an annotation:
scala> class ann(x: Int = 1) extends annotation.Annotation
scala> @ann() class K

scala> :power

scala> val info = typeOf[K].typeSymbol.annotations.head
val info: $r.intp.global.AnnotationInfo = ann(1)

scala> info.args.head.tpe
val res1: $r.intp.global.Type = Int(1) @scala.annotation.meta.defaultArg

scala> info.argIsDefault(info.args.head)
val res2: Boolean = true

@lrytz lrytz force-pushed the sd884 branch 2 times, most recently from ec8804d to 1a11ace Compare January 10, 2025 14:29
@lrytz
Copy link
Member Author

lrytz commented Jan 10, 2025

Added support for subclassing annotations, so class nodep extends nowarn("cat=deprecation") works now.

lrytz added 3 commits January 13, 2025 10:20
In an ordinary application `f(y = b, x = a)`, the compiler needs
to create a block with local variables

```
{
  val x$1 = b
  val x$2 = a
  f(x$2, x$1)
}
```

in order to preserve evaluation order (`b` before `a`).

For annotations `@ann(y = b, x = a)` this is not needed, and it
makes annotation processing more difficult / impossible.
... and use the defaults at callsites
Keep the named / default arguments transformation enabled when
using annotation classes in executable code.

In `new annotation(y = {println("foo"); 2})`, the explicit argument
needs to be executed before any defaults.
lrytz added 2 commits January 13, 2025 16:19
This adds infrastructure so that the compiler or compiler plugins
can support user-defined annotation subclasses.
@lrytz lrytz changed the title Fix AnnotationInfo when using named arguments Fix AnnotationInfo when using named/default arguments, support custom annotation subclasses Jan 13, 2025
@lrytz
Copy link
Member Author

lrytz commented Jan 13, 2025

Updated the PR description with all the changes.

I had to introduce a typer mode (ANNOTmode) because we still need to perform the named/default args transformation when using annotation classes in executable code, which is allowed (def foo = new annot(y = blah)).

@som-snytt want to take a look? will also pass it by the Scala 3 folks.

@He-Pin
Copy link
Contributor

He-Pin commented Jan 13, 2025

Thanks, Does the same problem exits in Scala 3?

@som-snytt
Copy link
Contributor

I'll take a look. I've been following a bit. It's always an event when lrytz adds a mode.

@SethTisue SethTisue added the release-notes worth highlighting in next release notes label Jan 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-notes worth highlighting in next release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix AnnotationInfo when using defaults
5 participants