Bug 266032 - [compiler] Java compiler resolves qualified this expressions incorrectly
Summary: [compiler] Java compiler resolves qualified this expressions incorrectly
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.3.2   Edit
Hardware: PC Mac OS X - Carbon (unsup.)
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks:
 
Reported: 2009-02-24 16:30 EST by Aaron Greenhouse CLA
Modified: 2022-05-29 17:53 EDT (History)
1 user (show)

See Also:


Attachments
The example class to compile and run (2.17 KB, text/plain)
2009-02-24 16:30 EST, Aaron Greenhouse CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Aaron Greenhouse CLA 2009-02-24 16:30:48 EST
Created attachment 126617 [details]
The example class to compile and run

I originally discovered this bug on
  MacOS X Version: 3.3.2 Build id: M20080221-1800
I have also verified that it is a bug on
  Windows Vista Version: 3.4.1 Build id: M20080911-1700

Steps To Reproduce:

1. Create a new Java project.

2. Create a new package "test"

3. Create a new class HeldAsExample (see the attachment):

package test;

import test.HeldAsExample.Container.Middle1;
import test.HeldAsExample.Container.Middle1.Middle2;

public class HeldAsExample {
  public int t;
  
  public static void useArray(final int[] array) {}
   
  public class Container {
    public int c;
    
    public class Super {
      public int f;
    }
    
    public class Middle1 {
      public int m1;
      
      public class Middle2 {
        public int m2;
                
        public void stuff1(final Container other, final Container expected) {
          System.out.println("Inside stuff1, Middle2.this = " + Middle2.this + " == this? " + (Middle2.this == this));
          System.out.println("Inside stuff1, Middle1.this = " + Middle1.this);
          System.out.println("Inside stuff1, Container.this = " + Container.this);
          System.out.println("Inside stuff1, HeldAsExample.this = " + HeldAsExample.this);

          /* The immediately enclosing instance of s6 is "this" (a Middle2 object)
           * The immediately enclosing instance with respect to Super is other.
           */
          final Super s6 = other. new Super() {
            {
              System.out.println("Inside ACE, Middle2.this = " + Middle2.this);
              System.out.println("Inside ACE, Middle1.this = " + Middle1.this);
              System.out.println("Inside ACE, Container.this = " + Container.this);
              System.out.println("Inside ACE, HeldAsExample.this = " + HeldAsExample.this);
              System.out.println((Container.this == expected) ? "CORRECT" : "INCORRECT!");
            }
          };
        }
      }
    }
  }
  
  public static void main(String[] args) {
    final HeldAsExample h1 = new HeldAsExample();
    System.out.println("h1 = " + h1);
    final Container c1 = h1. new Container();
    System.out.println("c1 = " + c1);
    final Middle1 m1_1 = c1. new Middle1();
    System.out.println("m1_1 = " + m1_1);
    final Middle2 m2_1 = m1_1. new Middle2();
    System.out.println("m2_1 = " + m2_1);
    
    final HeldAsExample h2 = new HeldAsExample();
    final Container c2 = h2. new Container();
    System.out.println("h2 = " + h2);
    System.out.println("c2 = " + c2);
    m2_1.stuff1(c2, c1);
  }
}

Run the main method from HeldAsExample.  The last line of the output will be "INCORRECT!".  An example of what I get is

h1 = test.HeldAsExample@6caf43
c1 = test.HeldAsExample$Container@2ac982
m1_1 = test.HeldAsExample$Container$Middle1@e89b94
m2_1 = test.HeldAsExample$Container$Middle1$Middle2@3a6727
h2 = test.HeldAsExample@4a65e0
c2 = test.HeldAsExample$Container@665753
Inside stuff1, Middle2.this = test.HeldAsExample$Container$Middle1$Middle2@3a6727 == this? true
Inside stuff1, Middle1.this = test.HeldAsExample$Container$Middle1@e89b94
Inside stuff1, Container.this = test.HeldAsExample$Container@2ac982
Inside stuff1, HeldAsExample.this = test.HeldAsExample@6caf43
Inside ACE, Middle2.this = test.HeldAsExample$Container$Middle1$Middle2@3a6727
Inside ACE, Middle1.this = test.HeldAsExample$Container$Middle1@e89b94
Inside ACE, Container.this = test.HeldAsExample$Container@665753
Inside ACE, HeldAsExample.this = test.HeldAsExample@6caf43
INCORRECT!

If you compile the same class with Sun's javac and execute it, you will see the last line of output will be "CORRECT".  An example of what I get is

h1 = test.HeldAsExample@6caf43
c1 = test.HeldAsExample$Container@2ac982
m1_1 = test.HeldAsExample$Container$Middle1@e89b94
m2_1 = test.HeldAsExample$Container$Middle1$Middle2@3a6727
h2 = test.HeldAsExample@4a65e0
c2 = test.HeldAsExample$Container@665753
Inside stuff1, Middle2.this = test.HeldAsExample$Container$Middle1$Middle2@3a6727 == this? true
Inside stuff1, Middle1.this = test.HeldAsExample$Container$Middle1@e89b94
Inside stuff1, Container.this = test.HeldAsExample$Container@2ac982
Inside stuff1, HeldAsExample.this = test.HeldAsExample@6caf43
Inside ACE, Middle2.this = test.HeldAsExample$Container$Middle1$Middle2@3a6727
Inside ACE, Middle1.this = test.HeldAsExample$Container$Middle1@e89b94
Inside ACE, Container.this = test.HeldAsExample$Container@2ac982
Inside ACE, HeldAsExample.this = test.HeldAsExample@6caf43
CORRECT

The difference is that when compiled with Eclipse, the 3rd line from the end, "Inside ACE, Container.this = ..." will print the same object id as the line "c2 = ...", and when compiled with Sun's javac, it will print the same object id as the line "c1 = ...".  

To the best of my understanding, the Eclipse compiler is incorrect.


More information:

Please see the Java Language Specification, 3rd Edition [JLS3], Sections 8.1.3, 15.8.4, and 15.9.2 for the definitions and algorithms used below.

What this example is actually doing is showing the ids of the lexically enclosing instances of two different objects

(1) An instance of test.HeldAsExample.Container.Super.Middle1.Middle2

(2) An instance of an anonymous class derived from test.heldAsExample.Container.Super that is created by a method executing on the first instance.

In particular, within the main method, we have the following distinct objects:

* h1, a HeldAsExample object
* c1, a Container object whose immediately enclosing instance (and therefore first lexically enclosing instance) is the object referenced by h1
* m1_1, a Middle1 object whose immediately enclosing instance (and therefore first lexically enclosing instance) is the object referenced by c1, whose second lexically enclosing instance is the object referenced by h1
* m2_1, a Middle2 object whose immediately enclosing instance (and therefore first lexically enclosing instance) is the object referenced by m1_1, whose second lexically enclosing instance is the object referenced by c1, and whose third lexically enclosing instance is the object referenced by h1

* h2, a second HeldAsExample object
* c2, a Container object whose immediately enclosing instance (and therefore first lexically enclosing instance) is the object referenced by h2

We then invoke "stuff1" on the object referenced by m2_1, and pass the two Container objects c2 to "other" and c1 to "expected".  (The method operates on other, the argument expected is only there for error checking.)

Method "stuff1" first prints the zeroth through third lexically enclosing instances of the receiver.  Because the receiver is the object referenced by m2_1, we get the ids of the objects referenced by m2_1, m1_1, c1, and h1, respectively.  So far, so good.

Method "stuff1" then creates an instance of an anonymous class whose superclass is test.heldAsExample.Container.Super.  This new expression is qualified by the a reference to the "other" parameter that is bound to same object as "c2" in main().  According to JLS3 Section 15.9.2, page 426, the immediately enclosing instance of this object should be object referenced by "this", which in this case is the object referenced by m2_1 in main().  So the 2nd through 4th lexically enclosing instances of this object should be the 1st through 3rd lexically enclosing instances of the object referenced by m2_1, namely the objects referenced by m1_1, c1, and h1, respectively.

The instance initializer block of the anonymous class prints out the ids of the object's first through fourth lexically enclosing instances as referenced by the qualified this expressions Middle2.this, Middle1.this, Container.this, and HeldAsExample.this, respectively.  Finally, it performs an explicit sanity check by comparing the object referenced by Container.this against the object passed to the parameter "expected".  In this case, we had passed the object referenced by c1 to expected, which as described above references the third lexically enclosing object of the anonymous class instance, and ought to be the object referenced by Container.this.

Sadly, what we see is that when compiled with the Eclipse compiler, Container.this within the anonymous class refers to object referenced by c2.  This is wrong.  What is interesting, however, is that the object referenced by c2 is the immediately enclosing instance with respect to class Super of the anonymous class instance.  Java *does not* provide a syntax for identifying immediately enclosing instances with respect to a super class.  Qualified this expression only refer to lexically enclosing instances as per JLS3 15.8.4.  I suspect that a cross reference in an internal compiler model is incorrect in this case.

This error means that programs compiled with Eclipse might behave differently than those compiled with javac.  Not good.
Comment 1 Eclipse Genie CLA 2020-05-28 18:12:42 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 2 Eclipse Genie CLA 2022-05-29 17:53:55 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.