From df00045c435196495bfebfa38000c56372fef19c Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Mon, 18 May 2026 09:23:27 +0000 Subject: [PATCH] 8384131: Lowering of array enhanced for loop misses a synthetic cast Reviewed-by: jlahoda --- .../com/sun/tools/javac/comp/Lower.java | 3 +- .../tools/javac/foreach/T8384131.java | 235 ++++++++++++++++++ 2 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 test/langtools/tools/javac/foreach/T8384131.java 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 be603ffc9ca..6b38765b81b 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 @@ -3568,7 +3568,7 @@ public class Lower extends TreeTranslator { * int #len = array.length; * int #i = 0; }; * #i < #len; i$++ ) { - * T v = arr$[#i]; + * T v = (T) arr$[#i]; * stmt; * } * } @@ -3604,6 +3604,7 @@ public class Lower extends TreeTranslator { Type elemtype = types.elemtype(tree.expr.type); JCExpression loopvarinit = make.Indexed(make.Ident(arraycache), make.Ident(index)).setType(elemtype); + loopvarinit = transTypes.coerce(attrEnv, loopvarinit, tree.var.type); JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(tree.var.mods, tree.var.name, tree.var.vartype, diff --git a/test/langtools/tools/javac/foreach/T8384131.java b/test/langtools/tools/javac/foreach/T8384131.java new file mode 100644 index 00000000000..f7c7274b7aa --- /dev/null +++ b/test/langtools/tools/javac/foreach/T8384131.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2026, 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 8384131 + * @summary Lowering of array enhanced for loop misses a synthetic cast + * @library /tools/lib + * @modules java.compiler + * jdk.jdeps/com.sun.tools.javap + * @build toolbox.JavapTask + * @run main T8384131 + */ +import java.util.List; +import java.util.stream.Collectors; + +import toolbox.JavapTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class T8384131 { + + static class C { } + interface I { + default void f() {} + } + + static class Test { + void testArray(Z[] arr) { + for (I i : arr) { + i.f(); + } + } + + void testList(List l) { + for (I i : l) { + i.f(); + } + } + } + + static class CI extends C implements I { } + + public static void main(String[] args) throws Exception { + // neg tests + expectCCE(() -> new Test().testArray(new C[] { new C() })); + expectCCE(() -> new Test().testList(List.of(new C()))); + + // pos tests + new Test().testArray(new CI[] { new CI() }); + checkPrimitiveConversionsResults(); + checkPrimitiveConversionsBytecode(); + } + + static void expectCCE(Runnable action) { + try { + action.run(); + throw new AssertionError("expected CCE"); + } catch (ClassCastException expected) { + // expected + } catch (IncompatibleClassChangeError wrong) { + throw new AssertionError("got ICCE", wrong); + } + } + + // + static int integerTypeVariableArrayToIntWideningReferenceAndUnboxing(T[] values) { + for (int i : values) { + return i; + } + return 0; + } + + static int shortTypeVariableArrayToIntWideningReferenceAndUnboxingAndPrimitiveWidening(T[] values) { + for (int i : values) { + return i; + } + return 0; + } + + static long intArrayToLongPrimitiveWidening(int[] values) { + for (long l : values) { + return l; + } + return 0L; + } + + static int integerArrayToIntUnboxing(Integer[] values) { + for (int i : values) { + return i; + } + return 0; + } + + static long integerArrayToLongUnboxingAndPrimitiveWidening(Integer[] values) { + for (long l : values) { + return l; + } + return 0L; + } + + static Integer intArrayToIntegerBoxing(int[] values) { + for (Integer i : values) { + return i; + } + return null; + } + + static Object intArrayToObjectBoxingAndWideningReference(int[] values) { + for (Object o : values) { + return o; + } + return null; + } + // + + static void checkPrimitiveConversionsResults() { + expectEquals(1, integerTypeVariableArrayToIntWideningReferenceAndUnboxing(new Integer[] { 1 })); + expectEquals(1, shortTypeVariableArrayToIntWideningReferenceAndUnboxingAndPrimitiveWidening(new Short[] { 1 })); + expectEquals(1L, intArrayToLongPrimitiveWidening(new int[] { 1 })); + expectEquals(1, integerArrayToIntUnboxing(new Integer[] { 1 })); + expectEquals(1L, integerArrayToLongUnboxingAndPrimitiveWidening(new Integer[] { 1 })); + expectEquals(Integer.valueOf(1), intArrayToIntegerBoxing(new int[] { 1 })); + expectEquals(Integer.valueOf(1), intArrayToObjectBoxingAndWideningReference(new int[] { 1 })); + } + + static void checkPrimitiveConversionsBytecode() throws Exception { + String out = new JavapTask(new ToolBox()) + .options("-c", "-private", "-s") + .classpath(System.getProperty("test.classes")) + .classes("T8384131") + .run() + .getOutputLines(Task.OutputKind.DIRECT) + .stream() + .collect(Collectors.joining("\n")); + + expect(out, "intArrayToLongPrimitiveWidening", + """ + descriptor: ([I)J + """, + """ + 14: iaload + 15: i2l + """); + expect(out, "integerArrayToIntUnboxing", + """ + descriptor: ([Ljava/lang/Integer;)I + """, + """ + 14: aaload + 15: invokevirtual # // Method java/lang/Integer.intValue:()I + """); + expect(out, "integerArrayToLongUnboxingAndPrimitiveWidening", + """ + descriptor: ([Ljava/lang/Integer;)J + """, + """ + 14: aaload + 15: invokevirtual # // Method java/lang/Integer.intValue:()I + 18: i2l + """); + expect(out, "intArrayToIntegerBoxing", + """ + descriptor: ([I)Ljava/lang/Integer; + """, + """ + 14: iaload + 15: invokestatic # // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + """); + expect(out, "intArrayToObjectBoxingAndWideningReference", + """ + descriptor: ([I)Ljava/lang/Object; + """, + """ + 14: iaload + 15: invokestatic # // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + """); + expect(out, "integerTypeVariableArrayToIntWideningReferenceAndUnboxing", + """ + descriptor: ([Ljava/lang/Integer;)I + """, + """ + 14: aaload + 15: invokevirtual # // Method java/lang/Integer.intValue:()I + """); + expect(out, "shortTypeVariableArrayToIntWideningReferenceAndUnboxingAndPrimitiveWidening", + """ + descriptor: ([Ljava/lang/Short;)I + """, + """ + 14: aaload + 15: invokevirtual # // Method java/lang/Short.shortValue:()S + """); + } + + static void expect(String out, String methodName, String... expectedFragments) { + int start = out.indexOf(methodName + "("); + int end = out.indexOf("\n\n", start); + String method = out + .substring(start, end == -1 ? out.length() : end) + .replaceAll("#\\d+\\s+//", "# //"); + for (String expected : expectedFragments) { + String fragment = expected.stripTrailing(); + if (!method.contains(fragment)) { + throw new AssertionError("Expected:\n" + fragment + "\nin " + methodName + ":\n" + method); + } + } + } + + static void expectEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + throw new AssertionError("Expected: " + expected + ", actual: " + actual); + } + } +}