Skip to content

Commit 9dfcd75

Browse files
committed
8334121: Anonymous class capturing two enclosing instances fails to compile
Reviewed-by: jlahoda, vromero
1 parent 000de30 commit 9dfcd75

File tree

4 files changed

+94
-50
lines changed

4 files changed

+94
-50
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ public Type erasure(Types types) {
358358
public Type externalType(Types types) {
359359
Type t = erasure(types);
360360
if (name == name.table.names.init && owner.hasOuterInstance()) {
361-
Type outerThisType = types.erasure(owner.type.getEnclosingType());
361+
Type outerThisType = owner.innermostAccessibleEnclosingClass().erasure(types);
362362
return new MethodType(t.getParameterTypes().prepend(outerThisType),
363363
t.getReturnType(),
364364
t.getThrownTypes(),
@@ -506,7 +506,23 @@ public boolean isInner() {
506506
*/
507507
public boolean hasOuterInstance() {
508508
return
509-
type.getEnclosingType().hasTag(CLASS) && (flags() & (INTERFACE | ENUM | RECORD | NOOUTERTHIS)) == 0;
509+
type.getEnclosingType().hasTag(CLASS) && (flags() & (INTERFACE | ENUM | RECORD)) == 0 &&
510+
((flags() & NOOUTERTHIS) == 0 || type.getEnclosingType().tsym.hasOuterInstance());
511+
}
512+
513+
/** If the class containing this symbol is a local or an anonymous class, then it might be
514+
* defined inside one or more pre-construction contexts, for which the corresponding enclosing
515+
* instance is considered inaccessible. This method return the class symbol corresponding to the
516+
* innermost enclosing type that is accessible from this symbol's class. Note: this method should
517+
* only be called after checking that {@link #hasOuterInstance()} returns {@code true}.
518+
*/
519+
public ClassSymbol innermostAccessibleEnclosingClass() {
520+
Assert.check(enclClass().hasOuterInstance());
521+
Type current = enclClass().type;
522+
while ((current.tsym.flags() & NOOUTERTHIS) != 0) {
523+
current = current.getEnclosingType();
524+
}
525+
return (ClassSymbol) current.getEnclosingType().tsym;
510526
}
511527

512528
/** The closest enclosing class of this symbol's declaration.

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -366,53 +366,9 @@ void visitSymbol(Symbol _sym) {
366366
if (v.getConstValue() == null) {
367367
addFreeVar(v);
368368
}
369-
} else {
370-
if (outerThisStack.head != null &&
371-
outerThisStack.head != _sym)
372-
visitSymbol(outerThisStack.head);
373369
}
374370
}
375371
}
376-
377-
/** If tree refers to a class instance creation expression
378-
* add all free variables of the freshly created class.
379-
*/
380-
public void visitNewClass(JCNewClass tree) {
381-
ClassSymbol c = (ClassSymbol)tree.constructor.owner;
382-
if (tree.encl == null &&
383-
c.hasOuterInstance() &&
384-
outerThisStack.head != null)
385-
visitSymbol(outerThisStack.head);
386-
super.visitNewClass(tree);
387-
}
388-
389-
/** If tree refers to a qualified this or super expression
390-
* for anything but the current class, add the outer this
391-
* stack as a free variable.
392-
*/
393-
public void visitSelect(JCFieldAccess tree) {
394-
if ((tree.name == names._this || tree.name == names._super) &&
395-
tree.selected.type.tsym != clazz &&
396-
outerThisStack.head != null)
397-
visitSymbol(outerThisStack.head);
398-
super.visitSelect(tree);
399-
}
400-
401-
/** If tree refers to a superclass constructor call,
402-
* add all free variables of the superclass.
403-
*/
404-
public void visitApply(JCMethodInvocation tree) {
405-
if (TreeInfo.name(tree.meth) == names._super) {
406-
Symbol constructor = TreeInfo.symbol(tree.meth);
407-
ClassSymbol c = (ClassSymbol)constructor.owner;
408-
if (c.hasOuterInstance() &&
409-
!tree.meth.hasTag(SELECT) &&
410-
outerThisStack.head != null)
411-
visitSymbol(outerThisStack.head);
412-
}
413-
super.visitApply(tree);
414-
}
415-
416372
}
417373

418374
ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
@@ -1586,7 +1542,7 @@ Name outerThisName(Type type, Symbol owner) {
15861542
}
15871543

15881544
private VarSymbol makeOuterThisVarSymbol(Symbol owner, long flags) {
1589-
Type target = types.erasure(owner.enclClass().type.getEnclosingType());
1545+
Type target = owner.innermostAccessibleEnclosingClass().erasure(types);
15901546
// Set NOOUTERTHIS for all synthetic outer instance variables, and unset
15911547
// it when the variable is accessed. If the variable is never accessed,
15921548
// we skip creating an outer instance field and saving the constructor
@@ -3097,7 +3053,7 @@ public void visitNewClass(JCNewClass tree) {
30973053
thisArg.type = tree.encl.type;
30983054
} else if (c.isDirectlyOrIndirectlyLocal()) {
30993055
// local class
3100-
thisArg = makeThis(tree.pos(), c.type.getEnclosingType().tsym);
3056+
thisArg = makeThis(tree.pos(), c.innermostAccessibleEnclosingClass());
31013057
} else {
31023058
// nested class
31033059
thisArg = makeOwnerThis(tree.pos(), c, false);
@@ -3303,7 +3259,7 @@ public void visitApply(JCMethodInvocation tree) {
33033259
((JCIdent) tree.meth).name = methName;
33043260
} else if (c.isDirectlyOrIndirectlyLocal() || methName == names._this){
33053261
// local class or this() call
3306-
thisArg = makeThis(tree.meth.pos(), c.type.getEnclosingType().tsym);
3262+
thisArg = makeThis(tree.meth.pos(), c.innermostAccessibleEnclosingClass());
33073263
} else if (currentClass.isStatic()) {
33083264
// super() call from static nested class - invalid
33093265
log.error(tree.pos(),

test/langtools/tools/javac/MethodParameters/LocalClassTest.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class LocalClassTest$1 -- anon
22
LocalClassTest$1.<init>(final this$0/*implicit*/, final j, final val$i/*synthetic*/)
33
class LocalClassTest$1CapturingLocal$1 -- anon
4-
LocalClassTest$1CapturingLocal$1.<init>(final val$this$0/*synthetic*/, final val$val$i/*synthetic*/)
4+
LocalClassTest$1CapturingLocal$1.<init>(final this$0/*implicit*/, final val$val$i/*synthetic*/)
55
LocalClassTest$1CapturingLocal$1.test()
66
class LocalClassTest$1CapturingLocal -- inner
77
LocalClassTest$1CapturingLocal.<init>(final this$0/*implicit*/, final j, final val$i/*synthetic*/)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
/*
24+
* @test
25+
* @bug 8334121
26+
* @summary Anonymous class capturing two enclosing instances fails to compile
27+
* @enablePreview
28+
*/
29+
30+
public class MultiLevelOuterInstance {
31+
32+
interface A {
33+
void run();
34+
}
35+
interface B {
36+
void run();
37+
}
38+
39+
class Inner1 {
40+
Inner1() {
41+
this(new A() {
42+
class Inner2 {
43+
Inner2() {
44+
this(new B() {
45+
public void run() {
46+
m();
47+
g();
48+
}
49+
});
50+
}
51+
52+
Inner2(B o) {
53+
o.run();
54+
}
55+
}
56+
57+
public void run() {
58+
new Inner2();
59+
}
60+
61+
void m() { }
62+
});
63+
}
64+
65+
Inner1(A o) { }
66+
}
67+
void g() { }
68+
69+
public static void main(String[] args) {
70+
new MultiLevelOuterInstance().new Inner1();
71+
}
72+
}

0 commit comments

Comments
 (0)