8383738: javac crashes with StackOverflowError when a class extends a type parameter

Reviewed-by: vromero, mcimadamore
This commit is contained in:
Dusan Balek 2026-05-29 05:00:00 +00:00 committed by Jan Lahoda
parent 114e3c6106
commit ea42cd938a
3 changed files with 126 additions and 8 deletions

View File

@ -895,7 +895,7 @@ public class Attr extends JCTree.Visitor {
tree.type :
attribType(tree, env);
try {
return checkBase(t, tree, env, classExpected, interfaceExpected, checkExtensible);
return tree.type = checkBase(t, tree, env, classExpected, interfaceExpected, checkExtensible);
} catch (CompletionFailure ex) {
chk.completionError(tree.pos(), ex);
return t;

View File

@ -268,17 +268,17 @@ public class Check {
* @param pos Position to be used for error reporting.
* @param required An internationalized string describing the type tag
* required.
* @param found The type that was found.
* @param type The type that was found.
*/
Type typeTagError(DiagnosticPosition pos, JCDiagnostic required, Object found) {
Type typeTagError(DiagnosticPosition pos, JCDiagnostic required, Type type) {
// this error used to be raised by the parser,
// but has been delayed to this point:
if (found instanceof Type type && type.hasTag(VOID)) {
if (type.hasTag(VOID)) {
log.error(pos, Errors.IllegalStartOfType);
return syms.errType;
}
log.error(pos, Errors.TypeFoundReq(found, required));
return types.createErrorType(found instanceof Type type ? type : syms.errType);
log.error(pos, Errors.TypeFoundReq(asTypeParam(type), required));
return types.createErrorType(type);
}
/** Report duplicate declaration error.
@ -645,7 +645,7 @@ public class Check {
if (!t.hasTag(CLASS) && !t.hasTag(ARRAY) && !t.hasTag(ERROR)) {
return typeTagError(pos,
diags.fragment(Fragments.TypeReqClassArray),
asTypeParam(t));
t);
} else {
return t;
}
@ -659,7 +659,7 @@ public class Check {
if (!t.hasTag(CLASS) && !t.hasTag(ERROR)) {
return typeTagError(pos,
diags.fragment(Fragments.TypeReqClass),
asTypeParam(t));
t);
} else {
return t;
}

View File

@ -0,0 +1,118 @@
/*
* 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 8383738
* @summary Check that javac does not crashes with StackOverflowError when compiling
* a class that extends a type parameter.
* @library /tools/lib
* @modules
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @build toolbox.ToolBox toolbox.JavacTask
* @run junit ${test.main.class}
*/
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import toolbox.JavacTask;
import toolbox.ToolBox;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import toolbox.Task;
public class StackOverflowWhenClassExtendsTypeParam {
Path base;
ToolBox tb = new ToolBox();
@Test
void testIsDerivedRaw() throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
new JavacTask(tb)
.options("-d", classes.toString())
.sources("""
class Test {
class A<T extends A<T>[]> implements T {}
class B<T extends A<T>[]> extends A<B<T>> {}
}
""")
.run(Task.Expect.FAIL)
.writeAll();
}
@Test
void testCheckClassBounds() throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
new JavacTask(tb)
.options("-d", classes.toString())
.sources("""
class Test {
interface I extends J<I> { }
interface J<T> extends T { }
}
""")
.run(Task.Expect.FAIL)
.writeAll();
}
@Test
void testTypesClosure() throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
new JavacTask(tb)
.options("-d", classes.toString())
.sources("""
public class Test {
public static void main(String[] args) {
LayerThree<Runnable, ? extends LayerThree<Runnable, ?>> testInstance = new LayerThree<>() {};
}
}
class LayerOne<A extends Number> {
}
class LayerTwo<B extends Comparable<B>, C extends LayerOne<B>> extends C {
}
class LayerThree<D extends Runnable, E extends LayerTwo<D, E>> extends E {
}
""")
.run(Task.Expect.FAIL)
.writeAll();
}
@BeforeEach
public void setUp(TestInfo info) {
base = Paths.get(".")
.resolve(info.getTestMethod()
.orElseThrow()
.getName());
}
}