From ea42cd938a4d2b93db87ca14c0f289e12d61c842 Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Fri, 29 May 2026 05:00:00 +0000 Subject: [PATCH] 8383738: javac crashes with StackOverflowError when a class extends a type parameter Reviewed-by: vromero, mcimadamore --- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../com/sun/tools/javac/comp/Check.java | 14 +-- ...tackOverflowWhenClassExtendsTypeParam.java | 118 ++++++++++++++++++ 3 files changed, 126 insertions(+), 8 deletions(-) create mode 100644 test/langtools/tools/javac/types/StackOverflowWhenClassExtendsTypeParam.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index a38ca7c01a1..30f08fad265 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -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; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 84920aaf556..34427765107 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -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; } diff --git a/test/langtools/tools/javac/types/StackOverflowWhenClassExtendsTypeParam.java b/test/langtools/tools/javac/types/StackOverflowWhenClassExtendsTypeParam.java new file mode 100644 index 00000000000..e84a5c8772e --- /dev/null +++ b/test/langtools/tools/javac/types/StackOverflowWhenClassExtendsTypeParam.java @@ -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[]> implements T {} + class B[]> extends A> {} + } + """) + .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 { } + interface J 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> testInstance = new LayerThree<>() {}; + } + } + + class LayerOne { + } + + class LayerTwo, C extends LayerOne> extends C { + } + + class LayerThree> extends E { + } + """) + .run(Task.Expect.FAIL) + .writeAll(); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +}