diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 0c155caa56d..98ad65fdc05 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -4090,19 +4090,20 @@ public class Types { return lub(classes); } } - // where - List erasedSupertypes(Type t) { - ListBuffer buf = new ListBuffer<>(); - for (Type sup : closure(t)) { - if (sup.hasTag(TYPEVAR)) { - buf.append(sup); - } else { - buf.append(erasure(sup)); - } - } - return buf.toList(); - } + public List erasedSupertypes(Type t) { + ListBuffer buf = new ListBuffer<>(); + for (Type sup : closure(t)) { + if (sup.hasTag(TYPEVAR)) { + buf.append(sup); + } else { + buf.append(erasure(sup)); + } + } + return buf.toList(); + } + + // where private Type arraySuperType; private Type arraySuperType() { // initialized lazily to avoid problems during compiler startup diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java index 2fb21a17a2d..7899b8335b6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java @@ -34,10 +34,12 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import java.util.function.ToIntBiFunction; import java.util.function.ToIntFunction; +import static com.sun.tools.javac.code.TypeTag.ARRAY; import static com.sun.tools.javac.code.TypeTag.BOT; import static com.sun.tools.javac.code.TypeTag.DOUBLE; import static com.sun.tools.javac.code.TypeTag.INT; import static com.sun.tools.javac.code.TypeTag.LONG; +import static com.sun.tools.javac.code.TypeTag.TYPEVAR; import static com.sun.tools.javac.jvm.ByteCodes.*; import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Class; import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Double; @@ -1824,14 +1826,42 @@ public class Code { } else if (types.isSubtype(t2, t1)) { return t1; } else { - Type lub = types.lub(t1, t2); - - if (lub.hasTag(BOT)) { + /* the most semantically correct approach here would be to invoke Types::lub + * and then erase the result. + * But this approach can be too slow for some complex cases, see JDK-8369654. + * This is why the method below leverages the fact that the result + * will be erased to produce a correct supertype using a simpler approach compared + * to a full blown lub. + */ + Type es = erasedSuper(t1, t2); + if (es == null || es.hasTag(BOT)) { throw Assert.error("Cannot find a common super class of: " + t1 + " and " + t2); } + return es; + } + } - return types.erasure(lub); + private Type erasedSuper(Type t1, Type t2) { + if (t1.hasTag(ARRAY) && t2.hasTag(ARRAY)) { + Type elem1 = types.elemtype(t1); + Type elem2 = types.elemtype(t2); + if (elem1.isPrimitive() || elem2.isPrimitive()) { + return (elem1.tsym == elem2.tsym) ? t1 : syms.serializableType; + } else { // both are arrays of references + return new ArrayType(erasedSuper(elem1, elem2), syms.arrayClass); + } + } else { + t1 = types.skipTypeVars(t1, false); + t2 = types.skipTypeVars(t2, false); + List intersection = types.intersect( + t1.hasTag(ARRAY) ? + List.of(syms.serializableType, syms.cloneableType, syms.objectType) : + types.erasedSupertypes(t1), + t2.hasTag(ARRAY) ? + List.of(syms.serializableType, syms.cloneableType, syms.objectType) : + types.erasedSupertypes(t2)); + return intersection.head; } } diff --git a/test/langtools/tools/javac/switchexpr/ExpressionSwitchComplexIntersectionTest.java b/test/langtools/tools/javac/switchexpr/ExpressionSwitchComplexIntersectionTest.java new file mode 100644 index 00000000000..917574287c5 --- /dev/null +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchComplexIntersectionTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025, 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 8369654 + * @summary javac OutOfMemoryError for complex intersection type + * @compile ExpressionSwitchComplexIntersectionTest.java + */ + +public class ExpressionSwitchComplexIntersectionTest { + interface WithMixin01 {} + interface WithMixin02 {} + interface WithMixin03 {} + interface WithMixin04 {} + interface WithMixin05 {} + interface WithMixin06 {} + interface WithMixin07 {} + interface WithMixin08 {} + interface WithMixin09 {} + interface WithMixin10 {} + interface WithMixin11 {} + interface WithMixin12 {} + interface WithMixin13 {} + interface WithMixin14 {} + interface WithMixin15 {} + interface WithMixin16 {} + interface WithMixin17 {} + interface WithMixin18 {} + interface WithMixin19 {} + interface WithMixin20 {} + + interface ClientA extends + WithMixin01, + WithMixin02, + WithMixin03, + WithMixin04, + WithMixin05, + WithMixin06, + WithMixin07, + WithMixin08, + WithMixin09, + WithMixin10, + WithMixin11, + WithMixin12, + WithMixin13, + WithMixin14, + WithMixin15, + WithMixin16, + WithMixin17, + WithMixin18, + WithMixin19, + WithMixin20 { + } + + interface ClientB extends + WithMixin01, + WithMixin02, + WithMixin03, + WithMixin04, + WithMixin05, + WithMixin06, + WithMixin07, + WithMixin08, + WithMixin09, + WithMixin10, + WithMixin11, + WithMixin12, + WithMixin13, + WithMixin14, + WithMixin15, + WithMixin16, + WithMixin17, + WithMixin18, + WithMixin19, + WithMixin20 { + } + + Object f1(boolean b, ClientA c1, ClientB c2) { + return b ? c1 : c2; + } + + Object f2(boolean b, ClientA[] array1, ClientB[] array2) { + return b ? array1 : array2; + } + + Object f3(boolean b, TA[] array1, TB[] array2) { + return b ? array1 : array2; + } + + Object f4(boolean b, TAA[] array1, TBB[] array2) { + return b ? array1 : array2; + } +}