diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index ca420eecdc5..4dd4f73a161 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -358,7 +358,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem public Type externalType(Types types) { Type t = erasure(types); if (name == name.table.names.init && owner.hasOuterInstance()) { - Type outerThisType = types.erasure(owner.type.getEnclosingType()); + Type outerThisType = owner.innermostAccessibleEnclosingClass().erasure(types); return new MethodType(t.getParameterTypes().prepend(outerThisType), t.getReturnType(), t.getThrownTypes(), @@ -506,7 +506,23 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem */ public boolean hasOuterInstance() { return - type.getEnclosingType().hasTag(CLASS) && (flags() & (INTERFACE | ENUM | RECORD | NOOUTERTHIS)) == 0; + type.getEnclosingType().hasTag(CLASS) && (flags() & (INTERFACE | ENUM | RECORD)) == 0 && + ((flags() & NOOUTERTHIS) == 0 || type.getEnclosingType().tsym.hasOuterInstance()); + } + + /** If the class containing this symbol is a local or an anonymous class, then it might be + * defined inside one or more pre-construction contexts, for which the corresponding enclosing + * instance is considered inaccessible. This method return the class symbol corresponding to the + * innermost enclosing type that is accessible from this symbol's class. Note: this method should + * only be called after checking that {@link #hasOuterInstance()} returns {@code true}. + */ + public ClassSymbol innermostAccessibleEnclosingClass() { + Assert.check(enclClass().hasOuterInstance()); + Type current = enclClass().type; + while ((current.tsym.flags() & NOOUTERTHIS) != 0) { + current = current.getEnclosingType(); + } + return (ClassSymbol) current.getEnclosingType().tsym; } /** The closest enclosing class of this symbol's declaration. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 6a5d75d7a89..1ac62183bbe 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -366,53 +366,9 @@ public class Lower extends TreeTranslator { if (v.getConstValue() == null) { addFreeVar(v); } - } else { - if (outerThisStack.head != null && - outerThisStack.head != _sym) - visitSymbol(outerThisStack.head); } } } - - /** If tree refers to a class instance creation expression - * add all free variables of the freshly created class. - */ - public void visitNewClass(JCNewClass tree) { - ClassSymbol c = (ClassSymbol)tree.constructor.owner; - if (tree.encl == null && - c.hasOuterInstance() && - outerThisStack.head != null) - visitSymbol(outerThisStack.head); - super.visitNewClass(tree); - } - - /** If tree refers to a qualified this or super expression - * for anything but the current class, add the outer this - * stack as a free variable. - */ - public void visitSelect(JCFieldAccess tree) { - if ((tree.name == names._this || tree.name == names._super) && - tree.selected.type.tsym != clazz && - outerThisStack.head != null) - visitSymbol(outerThisStack.head); - super.visitSelect(tree); - } - - /** If tree refers to a superclass constructor call, - * add all free variables of the superclass. - */ - public void visitApply(JCMethodInvocation tree) { - if (TreeInfo.name(tree.meth) == names._super) { - Symbol constructor = TreeInfo.symbol(tree.meth); - ClassSymbol c = (ClassSymbol)constructor.owner; - if (c.hasOuterInstance() && - !tree.meth.hasTag(SELECT) && - outerThisStack.head != null) - visitSymbol(outerThisStack.head); - } - super.visitApply(tree); - } - } ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) { @@ -1586,7 +1542,7 @@ public class Lower extends TreeTranslator { } private VarSymbol makeOuterThisVarSymbol(Symbol owner, long flags) { - Type target = types.erasure(owner.enclClass().type.getEnclosingType()); + Type target = owner.innermostAccessibleEnclosingClass().erasure(types); // Set NOOUTERTHIS for all synthetic outer instance variables, and unset // it when the variable is accessed. If the variable is never accessed, // we skip creating an outer instance field and saving the constructor @@ -3097,7 +3053,7 @@ public class Lower extends TreeTranslator { thisArg.type = tree.encl.type; } else if (c.isDirectlyOrIndirectlyLocal()) { // local class - thisArg = makeThis(tree.pos(), c.type.getEnclosingType().tsym); + thisArg = makeThis(tree.pos(), c.innermostAccessibleEnclosingClass()); } else { // nested class thisArg = makeOwnerThis(tree.pos(), c, false); @@ -3303,7 +3259,7 @@ public class Lower extends TreeTranslator { ((JCIdent) tree.meth).name = methName; } else if (c.isDirectlyOrIndirectlyLocal() || methName == names._this){ // local class or this() call - thisArg = makeThis(tree.meth.pos(), c.type.getEnclosingType().tsym); + thisArg = makeThis(tree.meth.pos(), c.innermostAccessibleEnclosingClass()); } else if (currentClass.isStatic()) { // super() call from static nested class - invalid log.error(tree.pos(), diff --git a/test/langtools/tools/javac/MethodParameters/LocalClassTest.out b/test/langtools/tools/javac/MethodParameters/LocalClassTest.out index bd25ff3a6be..3b95739e74a 100644 --- a/test/langtools/tools/javac/MethodParameters/LocalClassTest.out +++ b/test/langtools/tools/javac/MethodParameters/LocalClassTest.out @@ -1,7 +1,7 @@ class LocalClassTest$1 -- anon LocalClassTest$1.(final this$0/*implicit*/, final j, final val$i/*synthetic*/) class LocalClassTest$1CapturingLocal$1 -- anon -LocalClassTest$1CapturingLocal$1.(final val$this$0/*synthetic*/, final val$val$i/*synthetic*/) +LocalClassTest$1CapturingLocal$1.(final this$0/*implicit*/, final val$val$i/*synthetic*/) LocalClassTest$1CapturingLocal$1.test() class LocalClassTest$1CapturingLocal -- inner LocalClassTest$1CapturingLocal.(final this$0/*implicit*/, final j, final val$i/*synthetic*/) diff --git a/test/langtools/tools/javac/SuperInit/MultiLevelOuterInstance.java b/test/langtools/tools/javac/SuperInit/MultiLevelOuterInstance.java new file mode 100644 index 00000000000..e47e70a3bdc --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/MultiLevelOuterInstance.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * @test + * @bug 8334121 + * @summary Anonymous class capturing two enclosing instances fails to compile + * @enablePreview + */ + +public class MultiLevelOuterInstance { + + interface A { + void run(); + } + interface B { + void run(); + } + + class Inner1 { + Inner1() { + this(new A() { + class Inner2 { + Inner2() { + this(new B() { + public void run() { + m(); + g(); + } + }); + } + + Inner2(B o) { + o.run(); + } + } + + public void run() { + new Inner2(); + } + + void m() { } + }); + } + + Inner1(A o) { } + } + void g() { } + + public static void main(String[] args) { + new MultiLevelOuterInstance().new Inner1(); + } +}