Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 415019 - [1.8][compiler] Anonymous classes extending local classes have duplicate synthetic constructor arguments
Summary: [1.8][compiler] Anonymous classes extending local classes have duplicate synt...
Status: RESOLVED WONTFIX
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.4   Edit
Hardware: All Mac OS X
: P3 normal (vote)
Target Milestone: BETA J8   Edit
Assignee: Jesper Moller CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 406966
  Show dependency tree
 
Reported: 2013-08-14 03:20 EDT by Jesper Moller CLA
Modified: 2013-09-05 18:59 EDT (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jesper Moller CLA 2013-08-14 03:20:21 EDT
It may not be the biggest problem in the world, but given code such as this:

public class InnerLocalClassTest {
  void test() {
    class Local { }
    new Local() { };
  } 
}


ECJ generates the following constructor for the anonymous new Foo() {}

Compiled from "InnerLocalClassTest.java"
class InnerLocalClassTest$1 extends InnerLocalClassTest$1Local {
  final InnerLocalClassTest this$0;
    descriptor: LInnerLocalClassTest;

  InnerLocalClassTest$1(InnerLocalClassTest, InnerLocalClassTest);
    descriptor: (LInnerLocalClassTest;LInnerLocalClassTest;)V
    Code:
       0: aload_0       
       1: aload_2       
       2: putfield      #10                 // Field this$0:LInnerLocalClassTest;
       5: aload_0       
       6: aload_1       
       7: invokespecial #12                 // Method InnerLocalClassTest$1Local."<init>":(LInnerLocalClassTest;)V
      10: return        
}

Note how there are two arguments for the constructor, one for the reference, one for using in the constructor. javac only does this more elegantly:

Compiled from "InnerLocalClassTest.java"
class InnerLocalClassTest$1 extends InnerLocalClassTest$1Local {
  final InnerLocalClassTest this$0;
    descriptor: LInnerLocalClassTest;

  InnerLocalClassTest$1(InnerLocalClassTest);
    descriptor: (LInnerLocalClassTest;)V
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #1                  // Field this$0:LInnerLocalClassTest;
       5: aload_0       
       6: aload_1       
       7: invokespecial #2                  // Method InnerLocalClassTest$1Local."<init>":(LInnerLocalClassTest;)V
      10: return        
}

I can't tell if this could be an issue in other inner/anonymous cases.
Comment 1 Srikanth Sankaran CLA 2013-08-26 09:48:17 EDT
Jesper, is this a non-issue ? 

"2) In a class instance creation expression for a local class or anonymous 
class, §15.9.2 specifies the immediately enclosing instance of the local/anonymous
class. The local/anonymous class is necessarily emitted by the same compiler as
the class instance creation expression. That compiler can represent the 
immediately enclosing instance how ever it wishes. There is no need for the 
Java programming language to implicitly declare a parameter in the 
local/anonymous class's constructor."
Comment 2 Jesper Moller CLA 2013-08-26 09:55:31 EDT
Non-issue perhaps, but figuring out with argument is the mandated and which one is synthetic, is guesswork...
Comment 3 Srikanth Sankaran CLA 2013-08-26 10:08:33 EDT
(In reply to comment #2)
> Non-issue perhaps, but figuring out with argument is the mandated and which
> one is synthetic, is guesswork...

Actually, it directly follows from the last two sentences of the quoted text 
that none of the synthetic parameters in this case are mandated. Doesn't it ?

BTW, Are we skipping the parameter attributes for lambda methods ? javac seems to
do so.

See also: https://bugs.eclipse.org/bugs/show_bug.cgi?id=101512
Comment 4 Jesper Moller CLA 2013-08-26 10:16:04 EDT
(In reply to comment #3)

> Actually, it directly follows from the last two sentences of the quoted text 
> that none of the synthetic parameters in this case are mandated. Doesn't it ?

I guess so. Will add test about this to JEP118 work.

> BTW, Are we skipping the parameter attributes for lambda methods ? javac
> seems to
> do so.

Will check.

> See also: https://bugs.eclipse.org/bugs/show_bug.cgi?id=101512

Possible dupe, but this bug covers the arguments, not the fields.
Comment 5 Srikanth Sankaran CLA 2013-08-26 14:51:51 EDT
(In reply to comment #2)
> Non-issue perhaps, but figuring out with argument is the mandated and which
> one is synthetic, is guesswork...

(1) OK, I think I know what needs to be done *according to the present spec*
We need to look at the super class of the anonynmous class and if that is a
local class, it could not have been compiled by a different compiler and so 
ECJ is at liberty to "represent the  immediately enclosing instance how ever 
it wishes"

On the other hand, if the super class happens to be an inner class it could
have been compiled by a different compiler and so the enclosing reference
to the super class parameter must be received as a mandated implicit parameter.

HOWEVER, 8.8.9, point 3) reads
---
In a class instance creation expression for an anonymous class, and where 
the anonymous class's superclass is inner, §15.9.2 specifies the immediately 
enclosing instance with respect to the superclass. Similar to (1), the 
superclass may have been emitted by a compiler which is different than the 
compiler of the class instance creation expression. Therefore, there must be a 
standard way for the compiler of the creation expression to pass a reference
(representing the immediately enclosing instance with respect to the superclass) 
to the superclass's constructor. Consequently, the Java programming language 
deems that an anonymous class's constructor implicitly declares an initial 
parameter for the immediately enclosing instance with respect to the superclass.
---
but 8b100's behavior is confusing on the following code:

// --
public class X {
    void foo() {
        new Y().new Z() {
        };
    }
}
class Y {
    class Z {}
}
// --

  final X this$0;
    descriptor: LX;
    flags: ACC_FINAL, ACC_SYNTHETIC

  X$1(X, Y);
    descriptor: (LX;LY;)V
    flags:
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LX;
         5: aload_0
         6: aload_2
         7: dup
         8: invokevirtual #2                  // Method java/lang/Object.getClas
s:()Ljava/lang/Class;
        11: pop
        12: invokespecial #3                  // Method Y$Z."<init>":(LY;)V
        15: return
      LineNumberTable:
        line 3: 0
    MethodParameters:
      Name                           Flags
      this$0                         final mandated
      x0

Why would the anonymous class's own enclosing instances be mandated
while "x0" which I presume is the super classes enclosing instance
be flag less ? 

(2) Here is a bug:

// --
public class X {
    class Y {}
    private class Z {}	
}

 For the private constructor of Z(), we emit "final mandated this$0", it
should be "final synthetic this$0" ("The constructor of a non-private inner 
member class implicitly declares, as its first formal parameter, a variable 
representing the immediately enclosing instance")


(3) I believe javac 8b100 is in error when it emits a mandated this$0 parameter
for 

// --
public class X {
    void foo() {
        class Local {
        }
    }
}

this should be covered by 

"The local/anonymous class is necessarily emitted by the same compiler as 
the class instance creation expression. That compiler can represent the 
immediately enclosing instance how ever it wishes. There is no need for 
the Java programming language to implicitly declare a parameter in the 
local/anonymous class's constructor."

I'll ask in the mailing this about this.
Comment 6 Srikanth Sankaran CLA 2013-08-28 06:47:00 EDT
(In reply to comment #0)
> It may not be the biggest problem in the world, but given code such as this:
> 
> public class InnerLocalClassTest {
>   void test() {
>     class Local { }
>     new Local() { };
>   } 
> }

We have confirmation from the spec committee that this is covered by
the text quoted by comment#1. So please add a test to make sure that
we emit the two parameters as synthetic and we can close this one.

Also for the record:

(In reply to comment #5)

> but 8b100's behavior is confusing on the following code:
> 
> // --
> public class X {
>     void foo() {
>         new Y().new Z() {
>         };
>     }
> }
> class Y {
>     class Z {}
> }
> // --
> 
>   final X this$0;
>     descriptor: LX;
>     flags: ACC_FINAL, ACC_SYNTHETIC
> 
>   X$1(X, Y);
>     descriptor: (LX;LY;)V
>     flags:
>     Code:
>       stack=3, locals=3, args_size=3
>          0: aload_0
>          1: aload_1
>          2: putfield      #1                  // Field this$0:LX;
>          5: aload_0
>          6: aload_2
>          7: dup
>          8: invokevirtual #2                  // Method
> java/lang/Object.getClas
> s:()Ljava/lang/Class;
>         11: pop
>         12: invokespecial #3                  // Method Y$Z."<init>":(LY;)V
>         15: return
>       LineNumberTable:
>         line 3: 0
>     MethodParameters:
>       Name                           Flags
>       this$0                         final mandated
>       x0
> 
> Why would the anonymous class's own enclosing instances be mandated
> while "x0" which I presume is the super classes enclosing instance
> be flag less ? 

This is acknowledged to be a pair of bugs in javac. this$0 should be
synthetic and x0 should be mandated.

> (3) I believe javac 8b100 is in error when it emits a mandated this$0
> parameter
> for 
> 
> // --
> public class X {
>     void foo() {
>         class Local {
>         }
>     }
> }
> 
> this should be covered by 
> 
> "The local/anonymous class is necessarily emitted by the same compiler as 
> the class instance creation expression. That compiler can represent the 
> immediately enclosing instance how ever it wishes. There is no need for 
> the Java programming language to implicitly declare a parameter in the 
> local/anonymous class's constructor."
> 
> I'll ask in the mailing this about this.

This is acknowledged as a bug in javac too. This is covered by the same compiler
and its discretion bullet.

Please close if present behavior is correct after adding suitable tests if
not already there, Thanks!
Comment 7 Jesper Moller CLA 2013-08-29 21:01:57 EDT
(In reply to comment #6)
> (In reply to comment #5)
> 
> > but 8b100's behavior is confusing on the following code:
> >
> > // --
> > public class X {
> >     void foo() {
> >         new Y().new Z() {
> >         };
> >     }
> > }
> > class Y {
> >     class Z {}
> > }
> > // --
> >
> >   final X this$0;
> >     descriptor: LX;
> >     flags: ACC_FINAL, ACC_SYNTHETIC
> >
> >   X$1(X, Y);
> >     descriptor: (LX;LY;)V
> >     flags:
> >     Code:
> >       stack=3, locals=3, args_size=3
> >          0: aload_0
> >          1: aload_1
> >          2: putfield      #1                  // Field this$0:LX;
> >          5: aload_0
> >          6: aload_2
> >          7: dup
> >          8: invokevirtual #2                  // Method
> > java/lang/Object.getClas
> > s:()Ljava/lang/Class;
> >         11: pop
> >         12: invokespecial #3                  // Method Y$Z."<init>":(LY;)V
> >         15: return
> >       LineNumberTable:
> >         line 3: 0
> >     MethodParameters:
> >       Name                           Flags
> >       this$0                         final mandated
> >       x0
> >
> > Why would the anonymous class's own enclosing instances be mandated
> > while "x0" which I presume is the super classes enclosing instance
> > be flag less ?
> 
> This is acknowledged to be a pair of bugs in javac. this$0 should be
> synthetic and x0 should be mandated.

This does not make sense, since it is a constructor for an anonymous class, and so by nature up to the compiler's discretion.

It is true that x0 will then be used as an implicit argument to Y$Z(Y) as per §15.9.2, but it's place in the parameter list is still something the emitting compile may choose, and indeed it isn't even the initial argument.

I can take this to the list if you want.
Comment 8 Srikanth Sankaran CLA 2013-08-30 00:31:39 EDT
(In reply to comment #7)

> > This is acknowledged to be a pair of bugs in javac. this$0 should be
> > synthetic and x0 should be mandated.
> 
> This does not make sense, since it is a constructor for an anonymous class,
> and so by nature up to the compiler's discretion.
> 
> It is true that x0 will then be used as an implicit argument to Y$Z(Y) as
> per §15.9.2, but it's place in the parameter list is still something the
> emitting compile may choose, and indeed it isn't even the initial argument.
> 
> I can take this to the list if you want.

Please do. Thanks for catching this. I am inclined to agree with you. If you
concur, it may not be a bad idea to challenge the whole notion of ACC_MANDATED
flag: or at least question who is supposed to be consumer and what is the
expected/typical use case scenario for it is.
Comment 9 Jesper Moller CLA 2013-09-05 18:59:51 EDT
The tests for the correct generation of the synthetic constructor arguments is added as part of bug 406982.
The quirk about passing one extra argument is really not worth fixing, so i'm closing as such.