This page is no longer maintained — Please continue to the home page at www.scala-lang.org

scala.tools.nsc.symtab.AnnotationInfo does not return default values

6 replies
monster
Joined: 2011-10-22,
User offline. Last seen 42 years 45 weeks ago.

When processing annotations in a compiler plugin, I want to get the
value of it's parameters.
An AnnotationInfo is used to represent the annotation in the plugin.
Those parameters should be in the Map returned by "assocs", but only
the explicit parameters are in there.

My plugin runs after typer, so if default values are filled in a later
phase, that would be an explanation.

The point of default values are so that you always get a value, even
if the user doesn't give you one, so it's kind of pointless if you
cannot get the default value and have to store it doubly inside your
compiler plugin as well, in particular when you have many. My
annotation has currently *13* default parameters, and is going to get
some more...

I have found a post for Java that says you can do:

MyAnnotationClass.class.getMethod("myParamName").getDefaultValue()

But that's a pain, and I'm not even sure if it would work if the
annotation itself is in the unit that is being compiled. There must be
a better way to fix this. If there is no way, than I would consider
this a compiler bug.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: scala.tools.nsc.symtab.AnnotationInfo does not return defau

On Sat, Oct 22, 2011 at 10:11 AM, monster wrote:
> My plugin runs after typer, so if default values are filled in a later
> phase, that would be an explanation.

They're not. What values do you hope to find in there? There doesn't
appear to be anything restricting the defaults to constants. It's not
going to perform computation.

When I tried this out, I got no warning from this:

class bippy(x: Int = 1, y: String = "abc") extends StaticAnnotation
@bippy() class dingus()

But it gave me a lengthy warning when I did this:

class bippy(x: Int = 1, y: String = "abc") extends StaticAnnotation
@bippy(y = "def") class dingus()

./a.scala:6: warning: Usage of named or default arguments transformed
this annotation
constructor call into a block. The corresponding AnnotationInfo
will contain references to local values and default getters instead
of the actual argument trees
@bippy(y = "def") class dingus()
^
one warning found

I haven't had much luck in this department in the past (and most of
this is unrelated to default arguments, just with annotations in
general.) These comments are from elidable.scala:

/** This useless appearing code was necessary to allow people to use
* named constants for the elidable annotation. This is what it takes
* to convince the compiler to fold the constants: otherwise when it's
* time to check an elision level it's staring at a tree like
* {{{
* (Select(Level, Select(FINEST, Apply(intValue, Nil))))
* }}}
* instead of the number `300`.
*
* @since 2.8
*/

This is from AnnotationInfos:

// !!! when annotation arguments are not literals, but any sort of
// expression, there is a fair chance they will turn up here not as
// Literal(const) but some arbitrary AST.
def constantAtIndex(index: Int): Option[Constant] =
argAtIndex(index) collect { case Literal(x) => x }

The upshot is that if there is some underlying logic to it all, I
haven't hit upon it. Personally I'd consider it great progress if I
knew the compiler would yell at me when I'm doing something which
clearly requires things like "Ints" and "Strings" and it chuckles to
itself and hands me a bucket of Selects and Applys.

Oh by the way that line of code immediately above looks like a good
candidate for why you don't see any arguments. Because here's what
they look like to me (i.e. not like Constants.)

scala> intp("bob.dingus").annotations
res0: List[$r.intp.global.AnnotationInfo] =
List(bob.bippy(bob.this.bippy.init$default$1,
bob.this.bippy.init$default$2))

And indeed I can compute the defaults, although as you observe this
would be a little harder were it not already compiled:

scala> bob.bippy.init$default$1
res2: Int = 1

scala> bob.bippy.init$default$2
res3: String = abc

monster
Joined: 2011-10-22,
User offline. Last seen 42 years 45 weeks ago.
Re: scala.tools.nsc.symtab.AnnotationInfo does not return defaul

I'm afraid I failed to provide a critical information: I am using
annotations defined in *Java*, not in Scala. I am setting the default
value in the Java code as literals. I'm not even sure if anything else
is allowed, like using a constant defined in some other type. When I
learned Scala (2.8), I'm pretty sure the book said that you couldn't
define annotations in Scala, so I just got used to doing it in Java.
And since in that particular case the annotations are going to be used
in Java as well, I thought it made more sense that way, since most/all
Scala programmers can read Java code. But this might make the job of
the compiler harder.

CreateWrapper is my Java annotation. It has many parameters, all with
defaults. I tried this in REPL:

scala> @CreateWrapper(fieldName="test") class Toto {}
defined class Toto

scala> intp("Toto").annotations
res4: List[$r.intp.global.AnnotationInfo] =
List(CreateWrapper(fieldName= "test"))

As you can see, no defaults at all. They are simply not there. Anyway,
I managed to get them using the:

CreateWrapper.class.getMethod("fieldName").getDefaultValue()

work-around, but that's not pretty, and doubles the size of the code
needed to read the annotation parameters.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: scala.tools.nsc.symtab.AnnotationInfo does not return d

On Sun, Oct 23, 2011 at 3:43 AM, monster wrote:
> I'm afraid I failed to provide a critical information: I am using
> annotations defined in *Java*, not in Scala.

Oh, yes that seems relevant.

> scala> intp("Toto").annotations
> res4: List[$r.intp.global.AnnotationInfo] =
> List(CreateWrapper(fieldName= "test"))
>
> As you can see, no defaults at all. They are simply not there.

If you look at the java parser in scalac you will see it is very
primitive. It parses definitions, but it throws away expressions.
Assuming this is the relevant code, it looks quite like it is
disposing of them wholesale (after adding a marker annotation to
communicate that a default did exist.)

if (parentToken == AT && in.token == DEFAULT) {
val annot =
atPos(pos) {
New(Select(scalaDot(newTermName("runtime")),
tpnme.AnnotationDefaultATTR), List(List()))
}
mods1 = mods1 withAnnotations List(annot)
skipTo(SEMI)
accept(SEMI)
blankExpr

I looked at doing better and quickly came to the conclusion I don't
have time for it at the moment. I'm under the impression that the
range of legal java expressions which can appear as defaults to
annotations is severely constrained: if so, then perhaps this can be
made to work acceptably without a complete java expression parser.
Still, someone has to work it out.

(Here is some more code: I think it's for parsing arguments to
annotations at application sites. It's commented out and always has
been. Notice it invokes a mythical expression1() production, again
pointing toward the absence of a complete java parser as a limiting
factor.)

/*
def annotationArg() = {
val pos = in.token
if (in.token == IDENTIFIER && in.lookaheadToken == ASSIGN) {
val name = ident()
accept(ASSIGN)
atPos(pos) {
ValDef(Modifiers(Flags.JAVA), name, TypeTree(), elementValue())
}
} else {
elementValue()
}
}

def elementValue(): Tree =
if (in.token == AT) annotation()
else if (in.token == LBRACE) elementValueArrayInitializer()
else expression1()

def elementValueArrayInitializer() = {
accept(LBRACE)
val buf = new ListBuffer[Tree]
def loop() =
if (in.token != RBRACE) {
buf += elementValue()
if (in.token == COMMA) {
in.nextToken
loop()
}
}
loop()
accept(RBRACE)
buf.toList
}
*/

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: scala.tools.nsc.symtab.AnnotationInfo does not return defau

On Sat, Oct 22, 2011 at 10:11 AM, monster wrote:
> When processing annotations in a compiler plugin, I want to get the
> value of it's parameters.
> An AnnotationInfo is used to represent the annotation in the plugin.
> Those parameters should be in the Map returned by "assocs", but only
> the explicit parameters are in there.

OK, after a bit of exploratory effort, I can say that you should not
expect to find the defaults there. You should expect to find them
here:

scala> intp("bip.CreateWrapper").info.member("fieldName").annotations
res0: List[$r.intp.global.AnnotationInfo] =
List(()scala.runtime.AnnotationDefault)

And of course that's what you find right now, a marker that a default
existed, but no actual default. This was not too much trouble to
implement:

// CreateWrapper.java
package bip;

public @interface CreateWrapper {
String fieldName() default "[unassigned]";
}

// my branch
scala> intp("bip.CreateWrapper").info.member("fieldName").annotations
res0: List[$r.intp.global.AnnotationInfo] =
List(scala.runtime.AnnotationDefault("[unassigned]"))

The java source parser picks them up too, but it doesn't know what
they are so they come through as a String with the literal source code
between "default" and end of statement:

new scala.runtime.AnnotationDefault("\"[unassigned]\"")

Annotations aren't really my area so I'm not doing anything with this
pending input.

monster
Joined: 2011-10-22,
User offline. Last seen 42 years 45 weeks ago.
Re: scala.tools.nsc.symtab.AnnotationInfo does not return defaul

Thank you for taking the time to look into it. Since I want to stick
to a "standard" Scala implementation, and I have written my "work-
around" already, I'll stick to it for now. But at least I know I'm not
"doing anything wrong", and the defaults really aren't there.
Identifying a problem is the first step to fixing it, some this might
eventually improve if a commiter finds time to work on it.

Todd Vierling
Joined: 2011-04-27,
User offline. Last seen 42 years 45 weeks ago.
Re: scala.tools.nsc.symtab.AnnotationInfo does not return defau
On Wednesday, October 26, 2011 2:46:33 PM UTC-4, Paul Phillips wrote:

public @interface CreateWrapper {
  String fieldName() default "[unassigned]";
}

The java source parser picks them up too, but it doesn't know what
they are so they come through as a String with the literal source code
between "default" and end of statement:

  new scala.runtime.AnnotationDefault("\"[unassigned]\"")


It's not really possible to get better than this (raw source value) zwithout a more complete Java parser, because the following type of thing is valid Java:
public @interface Ann {  public class Foo {    public static final String X = "x";    public static final String Z = "z";  }

  String foo() default Foo.X + Foo.Z;}

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland