type_path of constructors for inner classes

Alex Buckley alex.buckley at oracle.com
Fri Oct 11 16:56:06 PDT 2013


On 10/11/2013 4:22 PM, Werner Dietl wrote:
> when reading "type of the newly constructed object" it seems to me
> like you're talking about an object creation - a "new" expression.
> There is a "new Inner();", but there is no type annotation there. Are
> you talking about line 7 or 5 in Joel's example?

I'm not talking about 'new' at all. I say "type of the newly constructed 
object" to indicate the type to which a TYPE_USE annotation in front of 
a ctor declaration applies. I got the phrase from page 3 of 
java-annotation-design.pdf:

"A type annotation is permitted in front of a constructor declaration, 
where declaration annotations are already permitted. In that location, a 
type annotation is treated as applying to the constructed object (which 
is different than the receiver, if any, of the constructor)."

> If I change line 7 to
>
>      public @Annot(5) Inner foo(@Annot(55) Test.Inner Test.Inner.this)
> { return new @Annot(555) Inner(); }
>
> And look at the generated bytecode, I see:
>
>        RuntimeVisibleTypeAnnotations:
>          0: #16(#17=I#23): NEW, offset=0, location=[INNER_TYPE]
>      RuntimeVisibleTypeAnnotations:
>        0: #16(#17=I#24): METHOD_RETURN, location=[INNER_TYPE]
>        1: #16(#17=I#25): METHOD_RECEIVER
>
> That behavior seems correct to me.

Yes. Note that @Annot(5) on the return type is really "Test.Inner 
@Annot(5)", hence there's a location clause, whereas @Annot(55) on the 
receiver parameter's type is truly on the Test of "Test.Inner", so 
there's no location clause.

> However, I think we were talking about the constructor result
> annotations, that is, about line 5.
> Do you consider the constructor name a simple type name?

Yes. As a matter of interest, the only place that SimpleTypeName appears 
in the language grammar is ConstructorDeclarator.

> In line 5:
>
> @Annot(0) Inner(@Annot(1) Test Test.this) {}
>
> It is not possible to make the outer type of the result explicit, that
> is, one cannot write:
>
> @Annot(666) Test. at Annot(0) Inner(@Annot(1) Test Test.this) {}
>
> Therefore, @Annot(1) is the only possible annotation for outer type of
> the constructor result.

You're saying that the "true" type of the newly constructed object at 
line 5 is:

   @Annot(1) Test . @Annot(0) Inner

and I agree. So why aren't there two annotations for the METHOD_RETURN 
target of the ctor, one with a location clause? You'd expect _at least_ 
the @Annot(0) to be emitted with a location clause.

> In your last paragraph, you have:
>
>> Our thinking is that a ctor does not have a return type, so getReturnType()
>> for a ctor's Executable really must give a NoType. Annotations on the ctor
>> with target_type=0x14 ("return type of method, or type of newly constructed
>> object") will be exposed on the NoType. It's not worth complicating with
>> receiver annotations.
>
> If the constructor result type is a NoType, wouldn't it be confusing
> if it had a location in bytecode? To me the location INNER_TYPE would
> only make sense if the result type were "@Anno(1) Test. @Anno(0)
> Inner".
> If we use NoType as the result type, I feel like the current behavior
> of javac is correct.

I see what you mean, but we're really talking about different things:

- A compiler can emit a "true" nested type (@Annot(1) Test . @Annot(0) 
Inner) as the type of a newly constructed object, and Core Reflection 
should be able to deal with it - j.l.r.Constructor inherits 
getAnnotatedReturnType() from j.l.r.Executable, so we made that method 
understand that a ctor may be annotated with the type of the constructed 
object.

- In the Language Model API, we give a NoType for a ctor's "return type" 
because in the JLS, a constructor does not have a return type. All you 
can do with the NoType is get its TYPE_USE annotations, which I would 
hope is @Annot(0). You can't distinguish between Test and Inner, but 
that's not a big deal because you're getting the annotation which was 
written at the appropriate location in the source code. An alternative 
would be to give a DeclaredType, since it would expose the "enclosing 
type" Test as distinct from Inner, but that would not be consistent with 
the JLS.

Alex

> Thoughts?
> cu, WMD.
>
>
>
> On Fri, Oct 11, 2013 at 6:40 PM, Alex Buckley <alex.buckley at oracle.com> wrote:
>> On 10/11/2013 12:30 PM, Werner Dietl wrote:
>>>
>>> The JSR 308 spec unfortunately doesn't make it clear what behavior is
>>> required, so maybe it's best to raise the issue to the EG.
>>
>>
>> When javac finds an annotation on the simple name of a nested type, it
>> appears to first expand the simple name to a qualified name, and then
>> describe the annotation with a target_path item. So, the @Annot(99)
>> annotation in:
>>
>> class Test {
>>    class Inner {}
>>    void m(@Annot(99) Inner i) {}
>> }
>>
>> is deemed to really be "Test. at Annot(99) Inner". A one-entry type_path
>> structure is emitted as the target_path item, and javap renders it as
>> "location=[INNER_TYPE]".
>>
>> That's all fine. The draft JLS/JVMS text for 308 - which I now regard as
>> "the spec" - doesn't say that simple names must be expanded because that's
>> the kind of thing compilers usually do automatically.
>>
>> In any case, I would have expected javac to treat the type of the newly
>> constructed object as Test.Inner too, and place @Annot(0) in the same nested
>> location. The type of the newly constructed object is just as much
>> Test.Inner as with the i parameter in my m method above.
>>
>>
>>> A constructor doesn't have a return type in the same sense as a method.
>>> One cannot have annotations on type arguments or outer types on a
>>> constructor result.
>>> The spec says:
>>>
>>> "Outer class annotations for a constructor result must be identical to
>>> those on the receiver, so they can be inferred from the annotations on
>>> the receiver."
>>>
>>> So not having the location seems OK, because there is no possible
>>> ambiguity.
>>
>>
>> I have not transferred this "must be identical" clause to the spec because I
>> don't know what it means. First, in a nested class declaration, you can't
>> write the type of the constructor result, so you can't write outer class
>> annotations. Second, when I see "must" in normative text, I look for the
>> error to be given if the clause is violated, but no error is mandated here.
>>
>> Please supply two declarations, one which you think should give an error and
>> one which should not.
>>
>>
>>> The JavaDoc for javax.lang.model.type.ExecutableType.getReturnType()
>>> currently says:
>>>
>>>        * Returns the return type of this executable.
>>>        * Returns a {@link NoType} with kind {@link TypeKind#VOID VOID}
>>>        * if this executable is not a method, or is a method that does not
>>>        * return a value."
>>>
>>> So I would assume that the result for an annotated constructor would be
>>> an annotated NoType, containing only the annotations on the result,
>>> without any locations.
>>>
>>> A better alternative might be that the result type of the constructor
>>> uses the actual return type instead of NoType and performs the inference
>>> of the annotations from the receiver type.
>>> It would then probably be easier and more consistent if the result
>>> annotations contained the location information.
>>
>>
>> Our thinking is that a ctor does not have a return type, so getReturnType()
>> for a ctor's Executable really must give a NoType. Annotations on the ctor
>> with target_type=0x14 ("return type of method, or type of newly constructed
>> object") will be exposed on the NoType. It's not worth complicating with
>> receiver annotations.
>>
>> Alex
>>
>>
>>> On 10/02/2013 03:32 PM, Joel Borggrén-Franck wrote:
>>>>
>>>> Hi Werner,
>>>>
>>>> When looking at the byte code for this class:
>>>>
>>>>     3 public class Test {
>>>>     4     class Inner {
>>>>     5         @Annot(0) public Inner(@Annot(1) Test Test.this) {}
>>>>     6
>>>>     7         public @Annot(5) Inner foo(@Annot(55) Test.Inner
>>>> Test.Inner.this) { return new Inner(); }
>>>>     8     }
>>>>     9 }
>>>>    10
>>>>    11 @Retention(RetentionPolicy.RUNTIME)
>>>>    12 @Target(ElementType.TYPE_USE)
>>>>    13 @interface Annot {
>>>>    14     int value();
>>>>    15 }
>>>>
>>>> javap output this for the method foo();
>>>>
>>>>     public Test$Inner foo();
>>>>       descriptor: ()LTest$Inner;
>>>> ...
>>>>       RuntimeVisibleTypeAnnotations:
>>>>         0: #13(#14=I#21): METHOD_RETURN, location=[INNER_TYPE]
>>>>         1: #13(#14=I#22): METHOD_RECEIVER
>>>>
>>>> and this for the ctor:
>>>>
>>>>    public Test$Inner(Test);
>>>>       descriptor: (LTest;)V
>>>>>>>>       RuntimeVisibleTypeAnnotations:
>>>>         0: #13(#14=I#15): METHOD_RETURN
>>>>
>>>> ^^^^^^ no location here ...
>>>>         1: #13(#14=I#16): METHOD_RECEIVER
>>>>
>>>> I think it is a bug that the ctor METHOD_RETURN lacks the
>>>> location/encoded type_path.
>>>>
>>>> What do you think?
>>>>
>>>> cheers
>>>> /Joel
>>>>
>>
>
>
>


More information about the type-annotations-dev mailing list