From 1ec74dca70b56076ad93c60186b8aee5ee9d4523 Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Tue, 12 May 2026 12:49:36 +0000 Subject: [PATCH] 8384235: javac crashes with AssertionError: Cannot add metadata to a void type when annotating void as a generic bound Reviewed-by: jlahoda --- .../sun/tools/javac/code/TypeAnnotations.java | 2 +- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../typeAnnotations/CrashOnVoidType.java | 270 ++++++++++++++++++ 3 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 test/langtools/tools/javac/annotations/typeAnnotations/CrashOnVoidType.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java index 452d15ed219..caa081505b6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java @@ -705,7 +705,7 @@ public class TypeAnnotations { @Override public Type visitType(Type t, List s) { - return t.annotatedType(s); + return t.hasTag(TypeTag.VOID) ? t : t.annotatedType(s); } }; 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 7b589b9fa0b..a38ca7c01a1 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 @@ -5273,7 +5273,7 @@ public class Attr extends JCTree.Visitor { public void visitAnnotatedType(JCAnnotatedType tree) { attribAnnotationTypes(tree.annotations, env); Type underlyingType = attribTree(tree.underlyingType, env, resultInfo); - if (underlyingType.getTag() == PACKAGE) { + if (underlyingType.getTag() == PACKAGE || underlyingType.getTag() == VOID) { result = tree.type = underlyingType; } else { Type annotatedType = underlyingType.preannotatedType(); diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/CrashOnVoidType.java b/test/langtools/tools/javac/annotations/typeAnnotations/CrashOnVoidType.java new file mode 100644 index 00000000000..44120e2a608 --- /dev/null +++ b/test/langtools/tools/javac/annotations/typeAnnotations/CrashOnVoidType.java @@ -0,0 +1,270 @@ +/* + * 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 8384235 + * @summary Check that javac does not creash when annotating void. + * @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 java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.ToolBox; + +public class CrashOnVoidType { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testVoidParam() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public class Test { + public void op(@Ann void p) {} + } + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:5:25: compiler.err.void.not.allowed.here", + "1 error")); + } + + @Test + void testVoidLocalVarType() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public class Test { + public void op() { + @Ann void l; + } + } + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:6:14: compiler.err.void.not.allowed.here", + "1 error")); + } + + @Test + void testVoidRecordComponent() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public record R(@Ann void v) {} + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "R.java:4:22: compiler.err.void.not.allowed.here", + "1 error")); + } + + @Test + void testVoidExceptionParam() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public class Test { + public void op() { + try { + } catch (@Ann void e) {} + } + } + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:7:23: compiler.err.void.not.allowed.here", + "1 error")); + } + + @Test + void testVoidUnion() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public class Test { + public void op() { + try { + } catch (Exception | @Ann void e) {} + } + } + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:7:30: compiler.err.illegal.start.of.type", + "1 error")); + } + + @Test + void testVoidTypeParam() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + import java.util.List; + + public class Test { + public List<@Ann void> l; + } + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:6:17: compiler.err.illegal.start.of.type", + "Test.java:6:22: compiler.err.void.not.allowed.here", + "2 errors")); + } + + @Test + void testVoidWildcardBound() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + import java.util.List; + + public class Test { + public List l; + } + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:6:17: compiler.err.illegal.start.of.type", + "Test.java:6:32: compiler.err.void.not.allowed.here", + "2 errors")); + } + + @Test + void testVoidTypeParamBound() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics") + .sources(""" + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + + public class Test { + public void op() {} + } + + @Target(ElementType.TYPE_USE) + @interface Ann {} + """) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Test.java:5:23: compiler.err.illegal.start.of.type", + "Test.java:5:28: compiler.err.void.not.allowed.here", + "2 errors")); + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +}