JDK-8356551 : Javac rejects receiver parameter in constructor of local class in early construction context
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 25
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2025-05-08
  • Updated: 2025-05-09
Related Reports
Relates :  
Description
The following program fails to compile:

```
class Outer1 {
   int x;

   void m() {
      class Outer2 {
          int y;

          Outer2() {
              class Local {
                  Local(Outer2 Outer2.this) { }
                  void m() {
                      int z = x;
                  }
              }
              super();
          }
      }
   }
}
```

This uses the obscure "receiver parameter" syntax -- described in JLS 8.4. There's two flavor of the syntax -- one is for regular method declaration and another one (the one in this example) has to do with the "enclosing this" parameter in an inner class constructor.


Turns out that when we are in a local class that is in the prologue of another class, that syntax is unusable -- the above example will fail with:

```
error: cannot reference this before supertype constructor has been called
                  Local(Outer2 Outer2.this) { }
                                     ^
1 error
```

This is a spurious error, which is probably triggered by the fact that javac interpres `Outer.this` as if it was an enclosing this access expression (while this is a simple parameter name).
Comments
A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/25153 Date: 2025-05-09 17:13:08 +0000
09-05-2025

[~mcimadamore], thanks for the explanation, now I get it. I was conflating "outer" terminology, i.e., JLS "outer" vs. compiler "outer".
09-05-2025

[~acobbs], from the JLS perspective, `Local` still has an enclosing instance, of type `Outer2`. Only, this instance is inaccessible. But here we don't want to access it -- we just want to maybe annotate its type (e.g. `Outer2.this` in this context means a different thing -- it's not an access expression, is just the name of a receiver parameter). I know that javac, for translation purposes, consider the true enclosing instance (as emitted in the `this$0` field) of `Local` to be Outer1) -- but from a language/JLS perspective, `Outer2` is the type of the enclosing instance. (and javac's attribution code agrees with this view -- Local.enclosingType() gives `Outer2`, not `Outer1`).
09-05-2025

[~liach], thanks for the heads-up. My information may be out-of-date, so I will leave this alone until I get more clarification. Thanks.
08-05-2025

[~mcimadamore], I'm not following you here. As I understand it, the class "Local" is not supposed to have any outer instance of type Outer2, so this code should not compile. Note this is a recent JLS change; previously "Local" did have an Outer2 outer instance; of course, this made it fairly useless because it couldn't actually be instantiated (at least, not unless you waited until after super() to do so).
08-05-2025

[~acobbs] For your info, we are considering to update the inner class model so only inner member classes have an enclosing this - for local or anonymous classes, the enclosing instance is just another captured variable and would be no different from any other local captures.
08-05-2025

Maybe you meant: "Local(Outer1 Outer1.this) { }" instead? I would agree that that should work. But it doesn't either, we get this weird double error: Outer1.java:10: error: the receiver type does not match the enclosing outer class type Local(Outer1 Outer1.this) { ^ required: Outer2 found: Outer1 Outer1.java:10: error: the receiver name does not match the enclosing outer class type Local(Outer1 Outer1.this) { ^ required: Outer2 found: Outer1 Note: Outer1.java uses preview features of Java SE 25. Note: Recompile with -Xlint:preview for details. 2 errors
08-05-2025