8369654: javac OutOfMemoryError for complex intersection type

Reviewed-by: liach, mcimadamore
This commit is contained in:
Vicente Romero 2025-11-03 18:32:06 +00:00
parent 1922c4fd6f
commit 9f972008ff
3 changed files with 161 additions and 16 deletions

View File

@ -4090,19 +4090,20 @@ public class Types {
return lub(classes);
}
}
// where
List<Type> erasedSupertypes(Type t) {
ListBuffer<Type> 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<Type> erasedSupertypes(Type t) {
ListBuffer<Type> 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

View File

@ -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<Type> 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;
}
}

View File

@ -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<T> {}
interface WithMixin02<T> {}
interface WithMixin03<T> {}
interface WithMixin04<T> {}
interface WithMixin05<T> {}
interface WithMixin06<T> {}
interface WithMixin07<T> {}
interface WithMixin08<T> {}
interface WithMixin09<T> {}
interface WithMixin10<T> {}
interface WithMixin11<T> {}
interface WithMixin12<T> {}
interface WithMixin13<T> {}
interface WithMixin14<T> {}
interface WithMixin15<T> {}
interface WithMixin16<T> {}
interface WithMixin17<T> {}
interface WithMixin18<T> {}
interface WithMixin19<T> {}
interface WithMixin20<T> {}
interface ClientA extends
WithMixin01<ClientA>,
WithMixin02<ClientA>,
WithMixin03<ClientA>,
WithMixin04<ClientA>,
WithMixin05<ClientA>,
WithMixin06<ClientA>,
WithMixin07<ClientA>,
WithMixin08<ClientA>,
WithMixin09<ClientA>,
WithMixin10<ClientA>,
WithMixin11<ClientA>,
WithMixin12<ClientA>,
WithMixin13<ClientA>,
WithMixin14<ClientA>,
WithMixin15<ClientA>,
WithMixin16<ClientA>,
WithMixin17<ClientA>,
WithMixin18<ClientA>,
WithMixin19<ClientA>,
WithMixin20<ClientA> {
}
interface ClientB extends
WithMixin01<ClientB>,
WithMixin02<ClientB>,
WithMixin03<ClientB>,
WithMixin04<ClientB>,
WithMixin05<ClientB>,
WithMixin06<ClientB>,
WithMixin07<ClientB>,
WithMixin08<ClientB>,
WithMixin09<ClientB>,
WithMixin10<ClientB>,
WithMixin11<ClientB>,
WithMixin12<ClientB>,
WithMixin13<ClientB>,
WithMixin14<ClientB>,
WithMixin15<ClientB>,
WithMixin16<ClientB>,
WithMixin17<ClientB>,
WithMixin18<ClientB>,
WithMixin19<ClientB>,
WithMixin20<ClientB> {
}
Object f1(boolean b, ClientA c1, ClientB c2) {
return b ? c1 : c2;
}
Object f2(boolean b, ClientA[] array1, ClientB[] array2) {
return b ? array1 : array2;
}
<TA extends ClientA, TB extends ClientB> Object f3(boolean b, TA[] array1, TB[] array2) {
return b ? array1 : array2;
}
<TA extends ClientA, TB extends ClientB, TAA extends TA, TBB extends TB> Object f4(boolean b, TAA[] array1, TBB[] array2) {
return b ? array1 : array2;
}
}