From jlahoda at openjdk.org Wed Oct 1 05:47:23 2025 From: jlahoda at openjdk.org (Jan Lahoda) Date: Wed, 1 Oct 2025 05:47:23 GMT Subject: RFR: 8364991: Incorrect not-exhaustive error [v2] In-Reply-To: References: Message-ID: > Consider this code: > > $ cat Test.java > package test; > public class Test { > private int test1(Root r) { > return switch (r) { > case Root(R2(R1 _), R2(R1 _)) -> 0; > case Root(R2(R1 _), R2(R2 _)) -> 0; > case Root(R2(R2 _), R2(R1 _)) -> 0; > case Root(R2(R2 _), R2 _) -> 0; > }; > } > sealed interface Base {} > record R1() implements Base {} > record R2(Base b1) implements Base {} > record Root(R2 b2, R2 b3) {} > } > > > javac (JDK 25) will produce a compile-time error for this code: > > $ javac test/Test.java > .../test/Test.java:4: error: the switch expression does not cover all possible input values > return switch (r) { > ^ > 1 error > > > This error is not correct according to the JLS. JLS defines a set of possible reductions of pattern sets, and if there exists a series of reductions from the pattern set into a pattern set that covers the selector type, the switch is exhaustive. > > One such reduction is that if there's a sub-set of (record) patterns that only differ in one component ("the mismatching component"), we can replace them with a (set of) patterns where this component is reduced, and the other components are unmodified. > > Such path exists here (every line shows a set of patterns that is being transformed): > > Root(R2(R1 _), R2(R1 _)), Root(R2(R1 _), R2(R2 _)), Root(R2(R2 _), R2(R1 _)), Root(R2(R2 _), R2 _) > => choosing the second component as the mismatching component, then we can reduce Root(R2(R1 _), R2(R1 _)), Root(R2(R1 _), R2(R2 _)) => Root(R2(R1 _), R2 _); as we can reduce R2(R1 _), R2(R2 _) to R2 _ > Root(R2(R1 _), R2 _), Root(R2(R2 _), R2(R1 _)), Root(R2(R2 _), R2 _) > => choosing the first component as the mismatching component, we can reduce Root(R2(R1 _), R2 _), Root(R2(R2 _), R2 _) => Root(R2 _, R2 _) > Root(R2 _, R2 _) > => > Root _ > => > exhaustive > > > The problem here is that in the first step, javac chooses this path: > > Root(R2(R1 _), R2(R1 _)), Root(R2(R1 _), R2(R2 _)), Root(R2(R2 _), R2(R1 _)), Root(R2(R2 _), R2 _) > => reduce Root(R2(R1 _), R2(R1 _)), Root(R2(R2 _), R2(R1 _)) => Root(R2 _, R2(R1 _)) > Root(R2 _, R2(R1 _)), Root(R2(R1 _), R2(R2 _)), Root(R2(R2 _), R2 _) > => dead end, as there are no two patterns that would have the same nested pattern in the same component > > > If javac would do full backtracking, it could go back, and choose the other path, and find out the switch is exhaustive. But, full naive backtracking is, I think, prohibitively too slow for even relatively small swit... Jan Lahoda has updated the pull request incrementally with one additional commit since the last revision: Simplifying the code as suggested. ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27247/files - new: https://git.openjdk.org/jdk/pull/27247/files/bed37c4f..0ee60862 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27247&range=01 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27247&range=00-01 Stats: 6 lines in 1 file changed: 0 ins; 4 del; 2 mod Patch: https://git.openjdk.org/jdk/pull/27247.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27247/head:pull/27247 PR: https://git.openjdk.org/jdk/pull/27247 From jlahoda at openjdk.org Wed Oct 1 11:19:18 2025 From: jlahoda at openjdk.org (Jan Lahoda) Date: Wed, 1 Oct 2025 11:19:18 GMT Subject: Integrated: 8367279: Test tools/javac/tree/TreePosTest.java timed out In-Reply-To: References: Message-ID: On Wed, 17 Sep 2025 08:20:09 GMT, Jan Lahoda wrote: > The `test/langtools/tools/javac/tree/TreePosTest.java` tries to parse all langtools tests, and started to time out recently in some cases. In the case of this test, we can try to use the `JavacTaskPool`, which pools and reuses javac instances, rather than creating (and initializing) a javac instance for each file. Given this test only parses the file, and even attributes them, this should be relatively safe. > > Locally, I can't confirm too significant time improvements (before this change ~6s, after this change ~4s), but it might help on CI. This pull request has now been integrated. Changeset: 3607e998 Author: Jan Lahoda URL: https://git.openjdk.org/jdk/commit/3607e9986f1582ebdae1b6ad2a13c1a9c239e0d6 Stats: 29 lines in 1 file changed: 6 ins; 8 del; 15 mod 8367279: Test tools/javac/tree/TreePosTest.java timed out Reviewed-by: asotona ------------- PR: https://git.openjdk.org/jdk/pull/27334 From jlahoda at openjdk.org Thu Oct 2 06:55:57 2025 From: jlahoda at openjdk.org (Jan Lahoda) Date: Thu, 2 Oct 2025 06:55:57 GMT Subject: Integrated: 8368848: JShell's code completion not always working for multi-snippet inputs In-Reply-To: References: Message-ID: On Mon, 29 Sep 2025 16:16:39 GMT, Jan Lahoda wrote: > Having a JShell input like: > > jshell> String s() { return "";} s(). > > > the code completion is not working for it. The reason is that the input is two snippets, but the code completion will interpret it as only one snippet, and a method declaration and method invocation cannot coexist inside one snippet (as one is a declaration that must appear outside of a method, and the invocation must be inside a method body or field initializer). > > The proposal herein is for the JShell's code completion to split the input into snippets, and create a wrapper/compilation unit based on all the snippets. This pull request has now been integrated. Changeset: 5251405c Author: Jan Lahoda URL: https://git.openjdk.org/jdk/commit/5251405ce9ab1cbd84b798a538cb3865ea4675e9 Stats: 140 lines in 4 files changed: 114 ins; 15 del; 11 mod 8368848: JShell's code completion not always working for multi-snippet inputs Reviewed-by: asotona ------------- PR: https://git.openjdk.org/jdk/pull/27552 From jlahoda at openjdk.org Thu Oct 2 09:10:08 2025 From: jlahoda at openjdk.org (Jan Lahoda) Date: Thu, 2 Oct 2025 09:10:08 GMT Subject: RFR: 8366968: Exhaustive switch expression rejected by for not covering all possible values [v2] In-Reply-To: References: Message-ID: > Consider case like (from the bug): > > class Demo { > > sealed interface Base permits Special, Value {} > > non-sealed interface Value extends Base {} > > sealed interface Special extends Base permits SpecialValue {} > > non-sealed interface SpecialValue extends Value, Special {} > > static int demo(final Base base) { > return switch (base) { > case final Value value -> 0; > // Uncommenting the following line will make javac accept this program > //case final Special value -> throw new AssertionError(); > }; > > } > > } > > > This fails to compile: > > /tmp/Demo.java:12: error: the switch expression does not cover all possible input values > return switch (base) { > ^ > 1 error > > > Note there is no instance of `Special` that would not be an instance of `Value` as well. I.e. covering `Value` will catch all input. > > Also, note that if `case Value` is replaced with `case SpecialValue`, javac also compile the code: > > case final Value value -> 0; > => > case final SpecialValue value -> 0; > > $ ~/tools/jdk/jdk-25/bin/javac /tmp/Demo.java > $ > > > Which shows the problem: replacing a type with a super type should not cause the switch to stop to be exhaustive (i.e. the switch is exhaustive for `SpecialValue`, but replacing it with its super type `Value`, the switch is no longer exhaustive for javac). > > javac contains a piece of code that searches through subtypes to handle diamond class hierarchies like the one above. But, when it searches for subtypes, it does a search through permitted subtypes of the type. And since `Value` is not sealed, this search will not find `SpecialValue`, and javac won't see the switch to be exhaustive. > > The proposal herein is to broaden the search, and consider all transitive permitted subtypes of the selector type, and filter subtypes of the current type from this set (if the current type is abstract, if it is not abstract, we can't do the subtype search at all). This should, I think, include all relevant subtypes. Jan Lahoda has updated the pull request incrementally with two additional commits since the last revision: - Removing trailing whitespace. - There are no relevant permitted subtypes of a non-abstract class, not even the class itself. ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27547/files - new: https://git.openjdk.org/jdk/pull/27547/files/4cf49874..64df6f43 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27547&range=01 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27547&range=00-01 Stats: 27 lines in 2 files changed: 25 ins; 0 del; 2 mod Patch: https://git.openjdk.org/jdk/pull/27547.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27547/head:pull/27547 PR: https://git.openjdk.org/jdk/pull/27547 From sherman at openjdk.org Fri Oct 3 19:10:20 2025 From: sherman at openjdk.org (Xueming Shen) Date: Fri, 3 Oct 2025 19:10:20 GMT Subject: RFR: 8365675: Add String Unicode Case-Folding Support Message-ID: ### Summary Case folding is a key operation for case-insensitive matching (e.g., string equality, regex matching), where the goal is to eliminate case distinctions without applying locale or language specific conversions. Currently, the JDK does not expose a direct API for Unicode-compliant case folding. Developers now rely on methods such as: **String.equalsIgnoreCase(String)** - Unicode-aware, locale-independent. - Implementation uses Character.toLowerCase(Character.toUpperCase(int)) per code point. - Limited: does not support 1:M mapping defined in Unicode case folding. **Character.toLowerCase(int) / Character.toUpperCase(int)** - Locale-independent, single code point only. - No support for 1:M mappings. **String.toLowerCase(Locale.ROOT) / String.toUpperCase(Locale.ROOT)** - Based on Unicode SpecialCasing.txt, supports 1:M mappings. - Intended primarily for presentation/display, not structural case-insensitive matching. - Requires full string conversion before comparison, which is less efficient and not intended for structural matching. **1:M mapping example, U+00DF (?)** - String.toUpperCase(Locale.ROOT, "?") ? "SS" - Case folding produces "ss", matching Unicode caseless comparison rules. jshell> "\u00df".equalsIgnoreCase("ss") $22 ==> false jshell> "\u00df".toUpperCase(Locale.ROOT).toLowerCase(Locale.ROOT).equals("ss") $24 ==> true ### Motivation & Direction Add Unicode standard-compliant case-less comparison methods to the String class, enabling & improving reliable and efficient Unicode-aware/compliant case-insensitive matching. - Unicode-compliant **full** case folding. - Simpler, stable and more efficient case-less matching without workarounds. - Brings Java's string comparison handling in line with other programming languages/libraries. This PR proposes to introduce the following comparison methods in `String` class - boolean equalsFoldCase(String anotherString) - int compareToFoldCase(String anotherString) - Comparator UNICODE_CASEFOLD_ORDER These methods are intended to be the preferred choice when Unicode-compliant case-less matching is required. *Note: An early draft also proposed a String.toCaseFold() method returning a new case-folded string. However, during review this was considered error-prone, as the resulting string could easily be mistaken for a general transformation like toLowerCase() and then passed into APIs where case-folding semantics are not appropriate. ### The New API /** * Compares this {@code String} to another {@code String} for equality, * using Unicode case folding. Two strings are considered equal * by this method if their case-folded forms are identical. *

* Case folding is defined by the Unicode Standard in * CaseFolding.txt, * including 1:M mappings. For example, {@code "Ma?e".equalsFoldCase("MASSE")} * returns {@code true}, since the character {@code U+00DF} (sharp s) folds * to {@code "ss"}. *

* Case folding is locale-independent and language-neutral, unlike * locale-sensitive transformations such as {@link #toLowerCase()} or * {@link #toUpperCase()}. It is intended for caseless matching, * searching, and indexing. * * @apiNote * This method is the Unicode-compliant alternative to * {@link #equalsIgnoreCase(String)}. It implements full case folding as * defined by the Unicode Standard, which may differ from the simpler * per-character mapping performed by {@code equalsIgnoreCase}. * For example: *

{@snippet lang=java :
     * String a = "Ma?e";
     * String b = "MASSE";
     * boolean equalsFoldCase = a.equalsFoldCase(b);       // returns true
     * boolean equalsIgnoreCase = a.equalsIgnoreCase(b);   // returns false
     * }
* * @param anotherString * The {@code String} to compare this {@code String} against * * @return {@code true} if the given object is not {@code null} and represents * the same sequence of characters as this string under Unicode case * folding; {@code false} otherwise. * * @see #compareToFoldCase(String) * @see #equalsIgnoreCase(String) * @since 26 */ public boolean equalsFoldCase(String anotherString) /** * Compares two strings lexicographically using Unicode case folding. * This method returns an integer whose sign is that of calling {@code compareTo} * on the Unicode case folded version of the strings. Unicode Case folding * eliminates differences in case according to the Unicode Standard, using the * mappings defined in * CaseFolding.txt, * including 1:M mappings, such as {@code"?"} ? {@code }"ss"}. *

* Case folding is a locale-independent, language-neutral form of case mapping, * primarily intended for caseless matching. Unlike {@link #compareToIgnoreCase(String)}, * which applies a simpler locale-insensitive uppercase mapping. This method * follows the Unicode full case folding, providing stable and * consistent results across all environments. *

* Note that this method does not take locale into account, and may * produce results that differ from locale-sensitive ordering. Use * {@link java.text.Collator} for locale-sensitive comparison. * * @apiNote * This method is the Unicode-compliant alternative to * {@link #compareToIgnoreCase(String)}. It implements the full case folding * as defined by the Unicode Standard, which may differ from the simpler * per-character mapping performed by {@code compareToIgnoreCase}. * For example: *

{@snippet lang=java :
     * String a = "Ma?e";
     * String b = "MASSE";
     * int cmpFoldCase = a.compareToFoldCase(b);     // returns 0
     * int cmpIgnoreCase = a.compareToIgnoreCase(b); // returns > 0
     * }
* * @param str the {@code String} to be compared. * @return a negative integer, zero, or a positive integer as the specified * String is greater than, equal to, or less than this String, * ignoring case considerations by case folding. * @see #equalsFoldCase(String) * @see #compareToIgnoreCase(String) * @see java.text.Collator * @since 26 */ public int compareToFoldCase(String str) /** * A Comparator that orders {@code String} objects as by * {@link #compareToFoldCase(String) compareToFoldCase()}. * * @see #compareToFoldCase(String) * @since 26 */ public static final Comparator UNICODE_CASEFOLD_ORDER; ### Usage Examples Sharp s (U+00DF) case-folds to "ss" "stra?e".equalsIgnoreCase("strasse"); // false "stra?e".compareToIgnoreCase("strasse"); // != 0 "stra?e".equalsFoldCase("strasse"); // true ### Performance The JMH microbenchmark StringCompareToIgnoreCase has been updated to compare performance of compareToFoldCase with the existing compareToIgnoreCase(). Benchmark Mode Cnt Score Error Units StringCompareToIgnoreCase.asciiGreekLower avgt 15 20.195 ? 0.300 ns/op StringCompareToIgnoreCase.asciiGreekLowerCF avgt 15 11.051 ? 0.254 ns/op StringCompareToIgnoreCase.asciiGreekUpperLower avgt 15 6.035 ? 0.047 ns/op StringCompareToIgnoreCase.asciiGreekUpperLowerCF avgt 15 14.786 ? 0.382 ns/op StringCompareToIgnoreCase.asciiLower avgt 15 17.688 ? 1.396 ns/op StringCompareToIgnoreCase.asciiLowerCF avgt 15 44.552 ? 0.155 ns/op StringCompareToIgnoreCase.asciiUpperLower avgt 15 13.069 ? 0.487 ns/op StringCompareToIgnoreCase.asciiUpperLowerCF avgt 15 58.684 ? 0.274 ns/op StringCompareToIgnoreCase.greekLower avgt 15 20.642 ? 0.082 ns/op StringCompareToIgnoreCase.greekLowerCF avgt 15 7.255 ? 0.271 ns/op StringCompareToIgnoreCase.greekUpperLower avgt 15 5.737 ? 0.013 ns/op StringCompareToIgnoreCase.greekUpperLowerCF avgt 15 11.100 ? 1.147 ns/op StringCompareToIgnoreCase.lower avgt 15 20.192 ? 0.044 ns/op StringCompareToIgnoreCase.lowerrCF avgt 15 11.257 ? 0.259 ns/op StringCompareToIgnoreCase.supLower avgt 15 54.801 ? 0.415 ns/op StringCompareToIgnoreCase.supLowerCF avgt 15 15.207 ? 0.418 ns/op StringCompareToIgnoreCase.supUpperLower avgt 15 14.431 ? 0.188 ns/op StringCompareToIgnoreCase.supUpperLowerCF avgt 15 19.149 ? 0.985 ns/op StringCompareToIgnoreCase.upperLower avgt 15 5.650 ? 0.051 ns/op StringCompareToIgnoreCase.upperLowerCF avgt 15 14.338 ? 0.352 ns/op StringCompareToIgnoreCase.utf16SubLower avgt 15 14.774 ? 0.200 ns/op StringCompareToIgnoreCase.utf16SubLowerCF avgt 15 2.669 ? 0.041 ns/op StringCompareToIgnoreCase.utf16SupUpperLower avgt 15 16.250 ? 0.099 ns/op StringCompareToIgnoreCase.utf16SupUpperLowerCF avgt 15 11.524 ? 0.327 ns/op ### Refs [Unicode Standard 5.18.4 Caseless Matching](https://www.unicode.org/versions/latest/core-spec/chapter-5/#G21790) [Unicode? Standard Annex #44: 5.6 Case and Case Mapping](https://www.unicode.org/reports/tr44/#Casemapping) [Unicode Technical Standard #18: Unicode Regular Expressions RL1.5: Simple Loose Matches](https://www.unicode.org/reports/tr18/#Simple_Loose_Matches) [Unicode SpecialCasing.txt](https://www.unicode.org/Public/UCD/latest/ucd/SpecialCasing.txt) [Unicode CaseFolding.txt](https://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt) ### Other Languages **Python string.casefold()** The str.casefold() method in Python returns a casefolded version of a string. Casefolding is a more aggressive form of lowercasing, designed to remove all case distinctions in a string, particularly for the purpose of caseless string comparisons. **Perl?s fc()** Returns the casefolded version of EXPR. This is the internal function implementing the \F escape in double-quoted strings. Casefolding is the process of mapping strings to a form where case differences are erased; comparing two strings in their casefolded form is effectively a way of asking if two strings are equal, regardless of case. Perl only implements the full form of casefolding, but you can access the simple folds using "casefold()" in Unicode::UCD] ad "prop_invmap()" in Unicode::UCD]. **ICU4J UCharacter.foldCase (Java)** Purpose: Provides extensions to the standard Java Character class, including support for more Unicode properties and handling of supplementary characters (code points beyond U+FFFF). Method Signature (String based): public static String foldCase(String str, int options) Method Signature (CharSequence & Appendable based): public static A foldCase(CharSequence src, A dest, int options, Edits edits) Key Features: Case Folding: Converts a string to its case-folded equivalent. Locale Independent: Case folding in UCharacter.foldCase is generally not dependent on locale settings. Context Insensitive: The mapping of a character is not affected by surrounding characters. Turkic Option: An option exists to include or exclude special mappings for Turkish/Azerbaijani text. Result Length: The resulting string can be longer or shorter than the original. Edits Recording: Allows for recording of edits for index mapping, styled text, and getting only changes. **u_strFoldCase (C/C++)** A lower-level C API function for case folding a string. Case Folding Options: Similar options as UCharacter.foldCase for controlling case folding behavior. Availability: Found in the ustring.h and unistr.h headers in the ICU4C library. ------------- Commit messages: - 8365675: Add String Unicode Case-Folding Support Changes: https://git.openjdk.org/jdk/pull/26892/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=26892&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8365675 Stats: 1279 lines in 12 files changed: 1116 ins; 137 del; 26 mod Patch: https://git.openjdk.org/jdk/pull/26892.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/26892/head:pull/26892 PR: https://git.openjdk.org/jdk/pull/26892 From acobbs at openjdk.org Mon Oct 6 16:10:57 2025 From: acobbs at openjdk.org (Archie Cobbs) Date: Mon, 6 Oct 2025 16:10:57 GMT Subject: RFR: 8369039: JDK-8348611 caused regression in Javac-Hot-Generate Message-ID: The refactoring in [JDK-8348611](https://bugs.openjdk.org/browse/JDK-8348611) caused a regression in compiler performance. In the the refactoring there were a couple of simple optimizations that were missed. When put in place, these seem to (mostly) address the performance issue. ------------- Commit messages: - Remove drive-by tweaks that aren\t the real issue. - Avoid using LintMapper when knowing rootLint suffices. - Avoid unnecessary recursion in LintMapper. Changes: https://git.openjdk.org/jdk/pull/27651/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27651&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8369039 Stats: 11 lines in 2 files changed: 11 ins; 0 del; 0 mod Patch: https://git.openjdk.org/jdk/pull/27651.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27651/head:pull/27651 PR: https://git.openjdk.org/jdk/pull/27651 From jlahoda at openjdk.org Mon Oct 6 17:27:35 2025 From: jlahoda at openjdk.org (Jan Lahoda) Date: Mon, 6 Oct 2025 17:27:35 GMT Subject: RFR: 8357809: Test jdk/jshell/JdiListeningExecutionControlTest.java failed with com.sun.jdi.connect.TransportTimeoutException Message-ID: The test timed-out while connecting to the remote agent over JDI. This PR proposes to try to increase the JDI connection timeout. ------------- Commit messages: - Adding missing comma. - 8357809: Test jdk/jshell/JdiListeningExecutionControlTest.java failed with com.sun.jdi.connect.TransportTimeoutException Changes: https://git.openjdk.org/jdk/pull/27653/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27653&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8357809 Stats: 11 lines in 3 files changed: 7 ins; 0 del; 4 mod Patch: https://git.openjdk.org/jdk/pull/27653.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27653/head:pull/27653 PR: https://git.openjdk.org/jdk/pull/27653 From jlahoda at openjdk.org Tue Oct 7 07:23:23 2025 From: jlahoda at openjdk.org (Jan Lahoda) Date: Tue, 7 Oct 2025 07:23:23 GMT Subject: RFR: 8340840: jshell ClassFormatError when making inner class static Message-ID: Consider a JShell interaction like: jshell> class O { class I {} } | created class O jshell> var i = new O().new I(); i ==> O$I at 77caeb3e jshell> class O { static class I {} } Exception in thread "main" java.lang.ClassFormatError: class not in class file format at jdk.jdi/com.sun.tools.jdi.VirtualMachineImpl.redefineClasses(VirtualMachineImpl.java:396) at jdk.jshell/jdk.jshell.execution.JdiExecutionControl.redefine(JdiExecutionControl.java:90) at jdk.jshell/jdk.jshell.Unit.doRedefines(Unit.java:312) at jdk.jshell/jdk.jshell.Eval.lambda$compileAndLoad$27(Eval.java:1120) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178) at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:722) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575) at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260) at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616) at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622) at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627) at jdk.jshell/jdk.jshell.Eval.lambda$compileAndLoad$29(Eval.java:1121) at jdk.jshell/jdk.jshell.TaskFactory.lambda$runTask$4(TaskFactory.java:213) at jdk.compiler/com.sun.tools.javac.api.JavacTaskPool.getTask(JavacTaskPool.java:193) at jdk.jshell/jdk.jshell.TaskFactory.runTask(TaskFactory.java:206) at jdk.jshell/jdk.jshell.TaskFactory.compile(TaskFactory.java:186) at jdk.jshell/jdk.jshell.Eval.compileAndLoad(Eval.java:1100) at jdk.jshell/jdk.jshell.Eval.declare(Eval.java:901) at jdk.jshell/jdk.jshell.Eval.eval(Eval.java:140) at jdk.jshell/jdk.jshell.JShell.eval(JShell.java:513) at jdk.jshell/jdk.internal.jshell.tool.JShellTool.processSource(JShellTool.java:3633) at jdk.jshell/jdk.internal.jshell.tool.JShellTool.processSourceCatchingReset(JShellTool.java:1353) at jdk.jshell/jdk.internal.jshell.tool.JShellTool.processInput(JShellTool.java:1251) at jdk.jshell/jdk.internal.jshell.tool.JShellTool.run(JShellTool.java:1222) at jdk.jshell/jdk.internal.jshell.tool.JShellTool.start(JShellTool.java:1005) at jdk.jshell/jdk.internal.jshell.tool.JShellToolBuilder.start(JShellToolBuilder.java:261) at jdk.jshell/jdk.internal.jshell.tool.JShellToolProvider.main(JShellToolProvider.java:120) There are two problems here (although the stack trace immediately only shows one of them): Redefining Classes --- When a snippet is redefined, JShell first tries to redefine it using JDI (`VirtualMachine.redefineClasses). This usually throws `UnsupportedOperationException` is the redefine cannot happen, and `JdiExecutionControl` handles that gracefully. JShell will recompile and reload the given snippet, and dependent snippets, under different names. But the `redefineClasses` method can also throw various `LinkageError`s. These are properly documented for the method: https://docs.oracle.com/en/java/javase/25/docs/api/jdk.jdi/com/sun/jdi/VirtualMachine.html#redefineClasses(java.util.Map) But `JdiExecutionControl` is not handling these `LinkageError`s. The proposed solution herein is to simply catch and handle the `LinkageError`s in the same way as the `UnsupportedOperationException` (and other exceptions). InnerClasses attribute data overriding information from sources --- Consider situation when compiling the third input: `class O { static class I {} }`. When this is being compiled, a classfile for `var i = new O().new I();` exists, and its `InnerClasses` attribute records `O.I` to be a non-`static` inner class. While compiling the code for `class O { static class I {} }`, the classfile for `var i` is also read from the classfile, and its `InnerClasses` attribute is read as well. And as a consequence, the `O.I` class will be marked as non-`static`, which conflicts with what is in the source file. This is not related to JShell as such, it can be reproduced with javac. Please see the `test/langtools/tools/javac/recovery/SourceAndInnerClassInconsistency.java` test. The proposal herein is to not use the information from the `InnerClasses` classfile attribute to manipulate classes that originate in a source file. More generally, I think the information from the source should always prevail over information from other/unrelated classfiles. Note the `InnerClasses` attribute is in a classfile that has no real relation to the source code that is being compiled, it is simply an classfile on the classpath. ------------- Commit messages: - Updating copyright year. - Fixing test. - 8340840: jshell ClassFormatError when making inner class static Changes: https://git.openjdk.org/jdk/pull/27665/files Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=27665&range=00 Issue: https://bugs.openjdk.org/browse/JDK-8340840 Stats: 141 lines in 4 files changed: 130 ins; 0 del; 11 mod Patch: https://git.openjdk.org/jdk/pull/27665.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27665/head:pull/27665 PR: https://git.openjdk.org/jdk/pull/27665 From redestad at openjdk.org Tue Oct 7 14:13:09 2025 From: redestad at openjdk.org (Claes Redestad) Date: Tue, 7 Oct 2025 14:13:09 GMT Subject: RFR: 8369039: JDK-8348611 caused regression in Javac-Hot-Generate In-Reply-To: References: Message-ID: On Mon, 6 Oct 2025 16:03:11 GMT, Archie Cobbs wrote: > The refactoring in [JDK-8348611](https://bugs.openjdk.org/browse/JDK-8348611) caused a regression in compiler performance. In the the refactoring there were a couple of simple optimizations that were missed. When put in place, these seem to (mostly) address the performance issue. I haven't been able to run this through our wider performance lab yet due to some issues on my end, but in local testing I'm not seeing that much of an improvement in my diagnostic tests (the tests are a bit noisy). Running the benchmark with `-prof gc` shows that the regression correlates with a 17% increase in allocation pressure. This PR only reduces allocation pressure by about from the elevated state 1%: 26-b11: 15.06 GB/op 26-b12: 17.61 GB/op pr/27651: 17.46 GB/op Looking at hot methods I see one of the lambdas in `LintMapper$FileInfo` (allocated in the constructor) being relatively hot and I think the abundant use of capturing lambdas in JDK-8348611 might be cause for some unexpected allocation overheads. For example desugaring the stream in the `FileInfo` constructor (which was the only thing I saw on `jfr view hot-methods` that seemed related to JDK-8348611): tree.defs.stream() .filter(this::isTopLevelDecl) .map(decl -> new Span(decl, tree.endPositions)) .forEach(unmappedDecls::add); to this: for (JCTree decl : tree.defs) { if (isTopLevelDecl(decl)) { unmappedDecls.add(new Span(decl, tree.endPositions)); } } .. reduces allocation to 16.68GB/op (reducing the relative increase in allocations by about 1/3 compared to pr/27651) ------------- PR Comment: https://git.openjdk.org/jdk/pull/27651#issuecomment-3377077619 From liach at openjdk.org Tue Oct 7 15:41:05 2025 From: liach at openjdk.org (Chen Liang) Date: Tue, 7 Oct 2025 15:41:05 GMT Subject: RFR: 8340840: jshell ClassFormatError when making inner class static In-Reply-To: References: Message-ID: On Tue, 7 Oct 2025 07:15:50 GMT, Jan Lahoda wrote: > Consider a JShell interaction like: > > jshell> class O { class I {} } > | created class O > > jshell> var i = new O().new I(); > i ==> O$I at 77caeb3e > > jshell> class O { static class I {} } > Exception in thread "main" java.lang.ClassFormatError: class not in class file format > at jdk.jdi/com.sun.tools.jdi.VirtualMachineImpl.redefineClasses(VirtualMachineImpl.java:396) > at jdk.jshell/jdk.jshell.execution.JdiExecutionControl.redefine(JdiExecutionControl.java:90) > at jdk.jshell/jdk.jshell.Unit.doRedefines(Unit.java:312) > at jdk.jshell/jdk.jshell.Eval.lambda$compileAndLoad$27(Eval.java:1120) > at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178) > at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:722) > at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) > at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) > at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575) > at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260) > at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616) > at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622) > at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627) > at jdk.jshell/jdk.jshell.Eval.lambda$compileAndLoad$29(Eval.java:1121) > at jdk.jshell/jdk.jshell.TaskFactory.lambda$runTask$4(TaskFactory.java:213) > at jdk.compiler/com.sun.tools.javac.api.JavacTaskPool.getTask(JavacTaskPool.java:193) > at jdk.jshell/jdk.jshell.TaskFactory.runTask(TaskFactory.java:206) > at jdk.jshell/jdk.jshell.TaskFactory.compile(TaskFactory.java:186) > at jdk.jshell/jdk.jshell.Eval.compileAndLoad(Eval.java:1100) > at jdk.jshell/jdk.jshell.Eval.declare(Eval.java:901) > at jdk.jshell/jdk.jshell.Eval.eval(Eval.java:140) > at jdk.jshell/jdk.jshell.JShell.eval(JShell.java:513) > at jdk.jshell/jdk.internal.jshell.tool.JShellTool.processSource(JShellTool.java:3633) > at jdk.jshell/jdk.internal.jshell.tool.JShellTool.processSourceCatchingReset(JShellTool.java:1353) > at jdk.jshell/jdk.internal.jshell.tool.JShellTool.processInput(JShellTool.java:1251) > at jdk.jshell/jdk.internal.jshell.tool.JShellTool.run(JShellTool.java:... src/jdk.jshell/share/classes/jdk/jshell/execution/JdiExecutionControl.java line 93: > 91: } catch (EngineTerminationException ex) { > 92: throw ex; > 93: } catch (Exception | LinkageError ex) { How was ClassFormatError previously thrown here? Was it wrapped in some exception? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/27665#discussion_r2411069586 From amaembo at gmail.com Tue Oct 7 16:12:22 2025 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 7 Oct 2025 18:12:22 +0200 Subject: Lambda and wildcard Message-ID: Hello! I'm investigating a seemingly weird compilation case. Consider the following Java interface: import java.util.function.Supplier; interface Main { interface X { X self(); } static X makeX() {return null;} static X create(Supplier supplier) {return null;} static X> methodRef() { return create(Main::makeX).self(); } static X> lambda() { return create(() -> makeX()).self(); } } I expect that either both methods 'methodRef' and 'lambda' should be compilable or both should be non-compilable. However, while 'methodRef' compiles, 'lambda' is rejected by compiler (using javac build 25+36-3489): Main.java:17: error: incompatible types: X> cannot be converted to X> return create(() -> makeX()).self(); ^ where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ? 1 error error: compilation failed Could you please help me and clarify whether this is an expected behavior or not? With best regards, Tagir Valeev -------------- next part -------------- An HTML attachment was scrubbed... URL: From zjx001202 at gmail.com Tue Oct 7 16:40:22 2025 From: zjx001202 at gmail.com (Glavo) Date: Wed, 8 Oct 2025 00:40:22 +0800 Subject: Lambda and wildcard In-Reply-To: References: Message-ID: Hi Tagir, This is expected behavior. See Chapter 15 of the Java Language Specification (Java SE 25 Edition) [1]: Unlike a lambda expression, a method reference can be congruent with a generic function type (that is, a function type that has type parameters). This is because the lambda expression would need to be able to declare type parameters, and no syntax supports this; while for a method reference, no such declaration is necessary. For example, the following program is legal: interface ListFactory { List make(); } ListFactory lf = ArrayList::new; List ls = lf.make(); List ln = lf.make(); Glavo [1]: https://docs.oracle.com/javase/specs/jls/se25/html/jls-15.html#jls-15.27 On Wed, Oct 8, 2025 at 12:12?AM Tagir Valeev wrote: > Hello! > > I'm investigating a seemingly weird compilation case. Consider the > following Java interface: > > import java.util.function.Supplier; > > interface Main { > interface X { > X self(); > } > > static X makeX() {return null;} > > static X create(Supplier supplier) {return null;} > > static X> methodRef() { > return create(Main::makeX).self(); > } > > static X> lambda() { > return create(() -> makeX()).self(); > } > } > > I expect that either both methods 'methodRef' and 'lambda' should be > compilable or both should be non-compilable. However, while 'methodRef' > compiles, 'lambda' is rejected by compiler (using javac build 25+36-3489): > > Main.java:17: error: incompatible types: X> cannot be converted > to X> > return create(() -> makeX()).self(); > ^ > where CAP#1 is a fresh type-variable: > CAP#1 extends Object from capture of ? > 1 error > error: compilation failed > > Could you please help me and clarify whether this is an expected behavior > or not? > > With best regards, > Tagir Valeev > -------------- next part -------------- An HTML attachment was scrubbed... URL: From acobbs at openjdk.org Tue Oct 7 17:42:32 2025 From: acobbs at openjdk.org (Archie Cobbs) Date: Tue, 7 Oct 2025 17:42:32 GMT Subject: RFR: 8369039: JDK-8348611 caused regression in Javac-Hot-Generate [v2] In-Reply-To: References: Message-ID: > The refactoring in [JDK-8348611](https://bugs.openjdk.org/browse/JDK-8348611) caused a regression in compiler performance. In the the refactoring there were a couple of simple optimizations that were missed. When put in place, these seem to (mostly) address the performance issue. Archie Cobbs has updated the pull request incrementally with two additional commits since the last revision: - Unstream loops. - Put back the drive-by tweaks; they seem to matter. ------------- Changes: - all: https://git.openjdk.org/jdk/pull/27651/files - new: https://git.openjdk.org/jdk/pull/27651/files/02d2e73f..816162e0 Webrevs: - full: https://webrevs.openjdk.org/?repo=jdk&pr=27651&range=01 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=27651&range=00-01 Stats: 21 lines in 1 file changed: 6 ins; 2 del; 13 mod Patch: https://git.openjdk.org/jdk/pull/27651.diff Fetch: git fetch https://git.openjdk.org/jdk.git pull/27651/head:pull/27651 PR: https://git.openjdk.org/jdk/pull/27651 From acobbs at openjdk.org Tue Oct 7 17:42:33 2025 From: acobbs at openjdk.org (Archie Cobbs) Date: Tue, 7 Oct 2025 17:42:33 GMT Subject: RFR: 8369039: JDK-8348611 caused regression in Javac-Hot-Generate In-Reply-To: References: Message-ID: On Tue, 7 Oct 2025 14:09:49 GMT, Claes Redestad wrote: >> The refactoring in [JDK-8348611](https://bugs.openjdk.org/browse/JDK-8348611) caused a regression in compiler performance. In the the refactoring there were a couple of simple optimizations that were missed. When put in place, these seem to (mostly) address the performance issue. > > I haven't been able to run this through our wider performance lab yet due to some issues on my end, but in local testing I'm not seeing that much of an improvement in my diagnostic tests (the tests are a bit noisy). > > Running the benchmark with `-prof gc` shows that the regression correlates with a 17% increase in allocation pressure. This PR only reduces allocation pressure by about 1% from the regressed state: > > > 26-b11: 15.06 GB/op > 26-b12: 17.61 GB/op > pr/27651: 17.46 GB/op > > > Looking at hot methods I see one of the lambdas in `LintMapper$FileInfo` (allocated in the constructor) being relatively hot and I think the abundant use of capturing lambdas in JDK-8348611 might be cause for some unexpected allocation overheads. > > For example desugaring the stream in the `FileInfo` constructor (which was the only thing I saw on `jfr view hot-methods` that seemed related to JDK-8348611): > > tree.defs.stream() > .filter(this::isTopLevelDecl) > .map(decl -> new Span(decl, tree.endPositions)) > .forEach(unmappedDecls::add); > > to this: > > for (JCTree decl : tree.defs) { > if (isTopLevelDecl(decl)) { > unmappedDecls.add(new Span(decl, tree.endPositions)); > } > } > > .. reduces allocation to 16.68GB/op (reducing the relative increase in allocations by about 1/3 compared to pr/27651) Hi @cl4es, > This PR only reduces allocation pressure by about 1% from the regressed state: Now you're moving the goalposts :) I was looking at the performance benchmark, not allocation pressure... FWIW this is what I saw on my laptop: JDK 25 Benchmark (stopStage) Mode Cnt Score Error Units SingleJavacBenchmark.compileHot Generate ss 10 14.566 ? 0.868 s/op JDK 26 Benchmark (stopStage) Mode Cnt Score Error Units SingleJavacBenchmark.compileHot Generate ss 10 16.403 ? 0.214 s/op JDK 26 + [this patch](https://github.com/openjdk/jdk/compare/master...archiecobbs:d098d010e8c8948bf728dcd3b95eb56f97268871) Benchmark (stopStage) Mode Cnt Score Error Units SingleJavacBenchmark.compileHot Generate ss 10 14.807 ? 0.381 s/op So that patch did seem to help resolve most of the benchmark difference. Are you not seeing the same thing? But also that patch differs from the one currently committed, in that it includes the `ArrayList` to `LinkedList` changes that were later taken out. Without those changes, things get slower again: Benchmark (stopStage) Mode Cnt Score Error Units SingleJavacBenchmark.compileHot Generate ss 10 15.832 ? 0.135 s/op So surprisingly, they have an effect. So I've put them back into the PR. > For example desugaring the stream in the FileInfo constructor ... reduces allocation `` Argh, this is frustrating. _Are we supposed to use the `Stream` API to help improve code clarity, or avoid it to prevent slowdowns?_ I've seen it argued both ways... `` Anyway, unstreaming the loop you suggested plus another one, plus the other stuff, yeilds this on my laptop: Benchmark (stopStage) Mode Cnt Score Error Units SingleJavacBenchmark.compileHot Generate ss 10 14.932 ? 0.370 s/op That is with [this patch](https://github.com/openjdk/jdk/compare/master...archiecobbs:816162e08f036d2c1613746d70481807dc8266ed). Let me know what you see on the allocation pressure front... thanks. ------------- PR Comment: https://git.openjdk.org/jdk/pull/27651#issuecomment-3377914846