From a73d012c727ecbd5fcf97a624fc969ba6305db5f Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Tue, 7 Feb 2023 16:23:52 +0000 Subject: [PATCH] 8295019: Cannot call a method with a parameter of a local class declared in a lambda Reviewed-by: mcimadamore --- .../sun/tools/javac/comp/DeferredAttr.java | 21 +++- .../TypeDeclarationInsideExpressionTest.java | 104 ++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 test/langtools/tools/javac/typeDeclarationInsideExpression/TypeDeclarationInsideExpressionTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java index d5400cdd82e..1ae84301274 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java @@ -464,10 +464,29 @@ public class DeferredAttr extends JCTree.Visitor { * disabled during speculative type-checking. */ JCTree attribSpeculative(JCTree tree, Env env, ResultInfo resultInfo) { + /* When performing speculative attribution on an argument expression, we should make sure that argument type + * cache does not get polluted with local types, as that leads to spurious type errors (see JDK-8295019) + */ return attribSpeculative(tree, env, resultInfo, treeCopier, - null, AttributionMode.SPECULATIVE, null); + null, AttributionMode.SPECULATIVE, !hasTypeDeclaration(tree) ? null : argumentAttr.withLocalCacheContext()); } + // where + private boolean hasTypeDeclaration(JCTree tree) { + TypeDeclVisitor typeDeclVisitor = new TypeDeclVisitor(); + typeDeclVisitor.scan(tree); + return typeDeclVisitor.result; + } + + private static class TypeDeclVisitor extends TreeScanner { + boolean result = false; + + @Override + public void visitClassDef(JCClassDecl that) { + result = true; + } + } + JCTree attribSpeculative(JCTree tree, Env env, ResultInfo resultInfo, LocalCacheContext localCache) { return attribSpeculative(tree, env, resultInfo, treeCopier, null, AttributionMode.SPECULATIVE, localCache); diff --git a/test/langtools/tools/javac/typeDeclarationInsideExpression/TypeDeclarationInsideExpressionTest.java b/test/langtools/tools/javac/typeDeclarationInsideExpression/TypeDeclarationInsideExpressionTest.java new file mode 100644 index 00000000000..c0785a86883 --- /dev/null +++ b/test/langtools/tools/javac/typeDeclarationInsideExpression/TypeDeclarationInsideExpressionTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023, 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 8295019 + * @summary Cannot call a method with a parameter of a local class declared in a lambda + * @compile TypeDeclarationInsideExpressionTest.java + */ + +class TypeDeclarationInsideExpressionTest { + class LambdaTest { + void run(Runnable r) {} + + void m() { + run(() -> { + class C { + static void takeC(C c) {} + static C giveC() { + return null; + } + } + C.takeC(C.giveC()); + + record R() { + static void takeR(R r) {} + static R giveR() { return null; } + } + R.takeR(R.giveR()); + + interface I { + static void takeI(I i) {} + static I giveI() { return null; } + } + I.takeI(I.giveI()); + + enum E { + A; + static void takeE(E e) {} + static E giveE() { return null; } + } + E.takeE(E.giveE()); + }); + } + } + + class SwitchExprTest { + void run(int i) {} + void m(int o) { + run(switch(o) { + case 1 -> { + class C { + static int takeC(C c) { return 0; } + static C giveC() { return null; } + } + yield C.takeC(C.giveC()); + } + case 2 -> { + record R() { + static int takeR(R r) { return 0; } + static R giveR() { return null; } + } + yield R.takeR(R.giveR()); + } + case 3 -> { + interface I { + static int takeI(I i) { return 0; } + static I giveI() { return null; } + } + yield I.takeI(I.giveI()); + } + case 4 -> { + enum E { + A; + static int takeE(E e) { return 0; } + static E giveE() { return null; } + } + yield E.takeE(E.giveE()); + } + default -> throw new AssertionError(); + }); + } + } +}