From gavin.bierman at oracle.com Mon Oct 3 12:48:44 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Oct 2022 12:48:44 +0000 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns Message-ID: Dear all, The draft JEPs for the next preview of the Pattern Matching for switch and Record Patterns features are now available at: Pattern matching for switch: https://bugs.openjdk.org/browse/JDK-8294285 Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 Comments welcomed! Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Oct 3 13:40:44 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 3 Oct 2022 15:40:44 +0200 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: References: Message-ID: Hello! > Remove support for named record patterns. This surprises me. Probably there was a discussion about the rationale behind this change? Could you please point me? Thanks. With best regards, Tagir Valeev On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman wrote: > > Dear all, > > The draft JEPs for the next preview of the Pattern Matching for switch and Record Patterns features are now available at: > > Pattern matching for switch: https://bugs.openjdk.org/browse/JDK-8294285 > Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 > > Comments welcomed! > Gavin From gavin.bierman at oracle.com Mon Oct 3 15:29:40 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Oct 2022 15:29:40 +0000 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: References: Message-ID: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> Hi Tagir, So it was a number of issues actually. First, there is a nasty ambiguity problem. Consider: record R(){} switch(e) { case R() when when (true) -> ... ... } The label could be parsed as either: case (R() when) when (true) -> or case (R()) when (when(true)) -> (where `when` is a static boolean method). There is another issue which is this variable declaration is the only one in the language that can?t be annotated or marked as `final` which feels like a design smell. None of the obvious solutions to this looked good. In most other languages with pattern matching they keep these two things - a destructing pattern and a naming pattern - separate. In both Haskell and Scala, they write `x at p` to ?name? a pattern p as x. So that seems like a possibility. But for now, we noted that in most cases you can rewrite pretty simply, e.g. case Point(var x, var y) when p.isVisible() -> can be rewritten: case Point p when p.isVisible() && p instanceof Point(var x, var y): ? or if it was in an instanceof: if (x instanceof Point p && p.isVisible() && p instanceof Point(var x, var y)) { ? } Neither of these versions read too badly. Thoughts? Gavin On 3 Oct 2022, at 14:40, Tagir Valeev > wrote: Hello! Remove support for named record patterns. This surprises me. Probably there was a discussion about the rationale behind this change? Could you please point me? Thanks. With best regards, Tagir Valeev On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman > wrote: Dear all, The draft JEPs for the next preview of the Pattern Matching for switch and Record Patterns features are now available at: Pattern matching for switch: https://bugs.openjdk.org/browse/JDK-8294285 Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 Comments welcomed! Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Mon Oct 3 16:05:01 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Oct 2022 16:05:01 +0000 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> Message-ID: <80C04B50-8BE9-4FCC-B51E-738FDC39BD46@oracle.com> case Point(var x, var y) when p.isVisible() -> Of course I meant: case Point(var x, var y) p when p.isVisible() -> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 3 16:25:48 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 3 Oct 2022 18:25:48 +0200 (CEST) Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> Message-ID: <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> > From: "Gavin Bierman" > To: "Tagir Valeev" > Cc: "amber-dev" , "amber-spec-experts" > > Sent: Monday, October 3, 2022 5:29:40 PM > Subject: Re: Draft JEPs: Pattern Matching for switch and Record Patterns > Hi Tagir, The main objection to remove the name of the record pattern is that it does not follow the principle of the data oriented programming. The idea is that the data is more important than the code, or said differently, if the data change by example a component is added to a record, the compiler should flag all the code that uses that record and ask the user to modify the code. So a case with a record pattern is better than just a type pattern, because unlike a type pattern, a record pattern validates the shape of a record case Point p // does not validate the shape of Point case Point(int x, int y) p // validates that a Point has two components x and y. When using virtual polymorphism, an operation is defined as an abstract method, so if the record shape changes, people will scan the rest of the record and change the implementation of the methods according to the new components. If the operation uses pattern matching, the record and the operation are not declared at the same place, so the compiler has to help users to find all the locations in the code that should be updated. > So it was a number of issues actually. First, there is a nasty ambiguity > problem. Consider: > record R(){} > switch(e) { > case R() when when (true) -> ... > ... > } > The label could be parsed as either: > case (R() when) when (true) -> > or > case (R()) when (when(true)) -> > (where ` when ` is a static boolean method). It's a usual issue with local keywords, we had the same kind of issue with the local keywords inside modules (transitive as a keyword or has a package name). A solution on top of my head is to make "when" a keyword for the whole case (everything in between "case" and "->"), so having to consecutive "when" is not syntactically valid. It's not the only option, and i don't think it's a showstopper. > There is another issue which is this variable declaration is the only one in the > language that can?t be annotated or marked as `final` which feels like a design > smell. None of the obvious solutions to this looked good. For me, a group pattern is not really different than a type pattern for this concern, the current syntax is with a type pattern is case final Point p -> so the syntax for a record pattern is case final Point(int x,int y) p -> It awkward and super verbose but it's a straight consequence of not having the binding always final. > In most other languages with pattern matching they keep these two things - a > destructing pattern and a naming pattern - separate. In both Haskell and Scala, > they write `x at p` to ?name? a pattern p as x. So that seems like a possibility. > But for now, we noted that in most cases you can rewrite pretty simply, e.g. > case Point(var x, var y) when p.isVisible() -> > can be rewritten: > case Point p > when p.isVisible() && p instanceof Point(var x, var y): ? > or if it was in an instanceof: > if (x instanceof Point p && p.isVisible() && p instanceof Point(var x, var y)) { > ? } > Neither of these versions read too badly. I disagree, a case ... does not exist in the vacuum but works and is read with the other cases. Here, following "case Point p when ... ", you will have a proper record pattern of Point to be exhaustive and the lack of common prefix between the two patterns makes the code hard to read. Point p = ... switch(p) { case Point p when p.isVisible() && p instanceof Point (var x, var y) -> ... case Point(int x, int y) -> ... } compared to Point p = ... switch(p) { case Point(int x, int y) p when p.isVisible() -> ... case Point(int x, int y) p -> ... } Here, it's clear that the first line is a peculiar case of the second line. > Thoughts? > Gavin R?mi >> On 3 Oct 2022, at 14:40, Tagir Valeev < [ mailto:amaembo at gmail.com | >> amaembo at gmail.com ] > wrote: >> Hello! >>> Remove support for named record patterns. >> This surprises me. Probably there was a discussion about the rationale >> behind this change? Could you please point me? Thanks. >> With best regards, >> Tagir Valeev >> On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman < [ mailto:gavin.bierman at oracle.com >> | gavin.bierman at oracle.com ] > wrote: >>> Dear all, >>> The draft JEPs for the next preview of the Pattern Matching for switch and >>> Record Patterns features are now available at: >>> Pattern matching for switch: [ https://bugs.openjdk.org/browse/JDK-8294285 | >>> https://bugs.openjdk.org/browse/JDK-8294285 ] >>> Record Patterns: [ https://bugs.openjdk.org/browse/JDK-8294078 | >>> https://bugs.openjdk.org/browse/JDK-8294078 ] >>> Comments welcomed! >>> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Oct 3 22:16:21 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 3 Oct 2022 18:16:21 -0400 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> Message-ID: <277f5d2c-d36a-e791-48d8-cd35e10bc8f7@oracle.com> I was skeptical at first too, but the weight of the points Gavin raises makes me feel that this aspect was (a) a little rushed and (b) not critical, so backing it out now gives us a chance to think it through further, and bring it back later in this or another form. On 10/3/2022 12:25 PM, Remi Forax wrote: > > > ------------------------------------------------------------------------ > > *From: *"Gavin Bierman" > *To: *"Tagir Valeev" > *Cc: *"amber-dev" , "amber-spec-experts" > > *Sent: *Monday, October 3, 2022 5:29:40 PM > *Subject: *Re: Draft JEPs: Pattern Matching for switch and Record > Patterns > > Hi Tagir, > > > The main objection to remove the name of the record pattern is that it > does not follow the principle of the data oriented programming. > The idea is that the data is more important than the code, or said > differently, if the data change by example a component is added to a > record, the compiler should flag all the code that uses that record > and ask the user to modify the code. > > So a case with a record pattern is better than just a type pattern, > because unlike a type pattern, a record pattern validates the shape of > a record > ? case Point p?? // does not validate the shape of Point > ? case Point(int x, int y) p? // validates that a Point has two > components x and y. > > When using virtual polymorphism, an operation is defined as an > abstract method, so if the record shape changes, people will scan the > rest of the record and change the implementation of the methods > according to the new components. If the operation uses pattern > matching, the record and the operation are not declared at the same > place, so the compiler has to help users to find all the locations in > the code that should be updated. > > > So it was a number of issues actually. First, there is a nasty > ambiguity problem. Consider: > > record R(){} > switch(e) { > ? ? case R() when when (true) -> ... > ? ? ... > } > > The label could be parsed as either: > > ? ? case (R() when) when (true) -> > > or > > ? ? case (R()) when (when(true)) -> > > (where `when` is a static boolean method). > > > It's a usual issue with local keywords, we had the same kind of issue > with the local keywords inside modules (transitive as a keyword or has > a package name). > > A solution on top of my head is to make "when" a keyword for the whole > case (everything in between "case" and "->"), so having to consecutive > "when" is not syntactically valid. > It's not the only option, and i don't think it's a showstopper. > > > > There is another issue which is this variable declaration is the > only one in the language that can?t be annotated or marked as > `final` which feels like a design smell. None of the obvious > solutions to this looked good. > > > For me, a group pattern is not really different than a type pattern > for this concern, > the current syntax is with a type pattern is > ? case final Point p -> > so the syntax for a record pattern is > ? case final Point(int x,int y) p -> > > It awkward and super verbose but it's a straight consequence of not > having the binding always final. > > > In most other languages with pattern matching they keep these two > things - a destructing pattern and a naming pattern - separate. In > both Haskell and Scala, they write `x at p` to ?name? a pattern p as > x. So that seems like a possibility. But for now, we noted that in > most cases you can rewrite pretty simply, e.g. > > case?Point(var x, var y) when p.isVisible() -> > > can be rewritten: > > ? ? case Point p > ? ? ? ? when p.isVisible() && p instanceof Point(var x, var y): ? > > or if it was in an instanceof: > > ? ? if (x instanceof Point p && p.isVisible() && p instanceof > Point(var x, var y)) {???} > > Neither of these versions read too badly. > > > I disagree, a case ... does not exist in the vacuum but works and is > read with the other cases. > Here, following "case Point p when ... ", you will have a proper > record pattern of Point to be exhaustive and the lack of common prefix > between the two patterns makes the code hard to read. > > ? Point p = ... > ? switch(p) { > ?? case Point p > ???? when p.isVisible() && p instanceof Point (var x, var y) -> ... > ?? case Point(int x, int y) -> ... > ? } > > compared to > ? Point p = ... > ? switch(p) { > ?? case Point(int x, int y) p when p.isVisible() -> ... > ?? case Point(int x, int y) p -> ... > ? } > > Here, it's clear that the first line is a peculiar case of the second > line. > > > Thoughts? > > Gavin > > > R?mi > > > > > On 3 Oct 2022, at 14:40, Tagir Valeev wrote: > > Hello! > > Remove support for named record patterns. > > > This surprises me. Probably there was a discussion about the > rationale > behind this change? Could you please point me? Thanks. > > With best regards, > Tagir Valeev > > On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman > wrote: > > > Dear all, > > The draft JEPs for the next preview of the Pattern > Matching for switch and Record Patterns features are now > available at: > > Pattern matching for switch: > https://bugs.openjdk.org/browse/JDK-8294285 > Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 > > Comments welcomed! > Gavin > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 3 23:38:21 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 4 Oct 2022 01:38:21 +0200 (CEST) Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <277f5d2c-d36a-e791-48d8-cd35e10bc8f7@oracle.com> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> <277f5d2c-d36a-e791-48d8-cd35e10bc8f7@oracle.com> Message-ID: <1473616214.18068711.1664840301143.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" , "Gavin Bierman" > Cc: "Tagir Valeev" , "amber-dev" , > "amber-spec-experts" > Sent: Tuesday, October 4, 2022 12:16:21 AM > Subject: Re: Draft JEPs: Pattern Matching for switch and Record Patterns > I was skeptical at first too, but the weight of the points Gavin raises makes me > feel that this aspect was (a) a little rushed and (b) not critical, so backing > it out now gives us a chance to think it through further, and bring it back > later in this or another form. I agree on (a), we need to put more work on it. Nothing here is critical so i suppose i agree on (b) but i would prefer to keep it because i want people to test mixing any patterns ('_') and record patterns, e.g. case Point p -> ... vs case Point(_,_) p -> ... vs case Point(int _, int _) p -> ... R?mi > On 10/3/2022 12:25 PM, Remi Forax wrote: >>> From: "Gavin Bierman" [ mailto:gavin.bierman at oracle.com | >>> ] >>> To: "Tagir Valeev" [ mailto:amaembo at gmail.com | ] >>> Cc: "amber-dev" [ mailto:amber-dev at openjdk.org | ] , >>> "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.org | >>> ] >>> Sent: Monday, October 3, 2022 5:29:40 PM >>> Subject: Re: Draft JEPs: Pattern Matching for switch and Record Patterns >>> Hi Tagir, >> The main objection to remove the name of the record pattern is that it does not >> follow the principle of the data oriented programming. >> The idea is that the data is more important than the code, or said differently, >> if the data change by example a component is added to a record, the compiler >> should flag all the code that uses that record and ask the user to modify the >> code. >> So a case with a record pattern is better than just a type pattern, because >> unlike a type pattern, a record pattern validates the shape of a record >> case Point p // does not validate the shape of Point >> case Point(int x, int y) p // validates that a Point has two components x and y. >> When using virtual polymorphism, an operation is defined as an abstract method, >> so if the record shape changes, people will scan the rest of the record and >> change the implementation of the methods according to the new components. If >> the operation uses pattern matching, the record and the operation are not >> declared at the same place, so the compiler has to help users to find all the >> locations in the code that should be updated. >>> So it was a number of issues actually. First, there is a nasty ambiguity >>> problem. Consider: >>> record R(){} >>> switch(e) { >>> case R() when when (true) -> ... >>> ... >>> } >>> The label could be parsed as either: >>> case (R() when) when (true) -> >>> or >>> case (R()) when (when(true)) -> >>> (where ` when ` is a static boolean method). >> It's a usual issue with local keywords, we had the same kind of issue with the >> local keywords inside modules (transitive as a keyword or has a package name). >> A solution on top of my head is to make "when" a keyword for the whole case >> (everything in between "case" and "->"), so having to consecutive "when" is not >> syntactically valid. >> It's not the only option, and i don't think it's a showstopper. >>> There is another issue which is this variable declaration is the only one in the >>> language that can?t be annotated or marked as `final` which feels like a design >>> smell. None of the obvious solutions to this looked good. >> For me, a group pattern is not really different than a type pattern for this >> concern, >> the current syntax is with a type pattern is >> case final Point p -> >> so the syntax for a record pattern is >> case final Point(int x,int y) p -> >> It awkward and super verbose but it's a straight consequence of not having the >> binding always final. >>> In most other languages with pattern matching they keep these two things - a >>> destructing pattern and a naming pattern - separate. In both Haskell and Scala, >>> they write `x at p` to ?name? a pattern p as x. So that seems like a possibility. >>> But for now, we noted that in most cases you can rewrite pretty simply, e.g. >>> case Point(var x, var y) when p.isVisible() -> >>> can be rewritten: >>> case Point p >>> when p.isVisible() && p instanceof Point(var x, var y): ? >>> or if it was in an instanceof: >>> if (x instanceof Point p && p.isVisible() && p instanceof Point(var x, var y)) { >>> ? } >>> Neither of these versions read too badly. >> I disagree, a case ... does not exist in the vacuum but works and is read with >> the other cases. >> Here, following "case Point p when ... ", you will have a proper record pattern >> of Point to be exhaustive and the lack of common prefix between the two >> patterns makes the code hard to read. >> Point p = ... >> switch(p) { >> case Point p >> when p.isVisible() && p instanceof Point (var x, var y) -> ... >> case Point(int x, int y) -> ... >> } >> compared to >> Point p = ... >> switch(p) { >> case Point(int x, int y) p when p.isVisible() -> ... >> case Point(int x, int y) p -> ... >> } >> Here, it's clear that the first line is a peculiar case of the second line. >>> Thoughts? >>> Gavin >> R?mi >>>> On 3 Oct 2022, at 14:40, Tagir Valeev < [ mailto:amaembo at gmail.com | >>>> amaembo at gmail.com ] > wrote: >>>> Hello! >>>>> Remove support for named record patterns. >>>> This surprises me. Probably there was a discussion about the rationale >>>> behind this change? Could you please point me? Thanks. >>>> With best regards, >>>> Tagir Valeev >>>> On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman < [ mailto:gavin.bierman at oracle.com >>>> | gavin.bierman at oracle.com ] > wrote: >>>>> Dear all, >>>>> The draft JEPs for the next preview of the Pattern Matching for switch and >>>>> Record Patterns features are now available at: >>>>> Pattern matching for switch: [ https://bugs.openjdk.org/browse/JDK-8294285 | >>>>> https://bugs.openjdk.org/browse/JDK-8294285 ] >>>>> Record Patterns: [ https://bugs.openjdk.org/browse/JDK-8294078 | >>>>> https://bugs.openjdk.org/browse/JDK-8294078 ] >>>>> Comments welcomed! >>>>> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Oct 8 07:07:31 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 8 Oct 2022 03:07:31 -0400 Subject: Compiler could be smarter when dealing with exhaustive switch and final variables? Message-ID: Hello Amber Dev Team, When if/else statements and final variables combine, the compiler can make some helpful assertions. Consider the following code. sealed interface SomeInterface {} record First() implements SomeInterface {} record Second() implements SomeInterface {} private void exhaustiveIfWithFinalUninitialized() { SomeInterface abc = new First(); final int numA; final int numB; if (abc instanceof First) { numA = -1; numB = -1; } else if (abc instanceof Second) { numA = -2; numB = -2; } else { throw new IllegalStateException("This shouldn't be possible!"); } System.out.println(numA + ""); System.out.println(numB + ""); } The above code compiles with no errors whatsoever. The compiler is smart enough to realize that, no matter what, numA and numB will most certainly be populated by the time we reach the print statements below the if statements. However, the compiler does not make the same realization when working with exhaustive switches. But since the switch statement is exhaustive, shouldn't the compiler be able to make the same realization here? If anything, the switch should be capable of all of the above and more, correct? private void exhaustiveSwitchWithFinalUninitialized() { final int numA; final int numB; SomeInterface abc = new First(); switch (abc) //compiler error unless we uncomment the default { case First f -> { numA = 1; numB = 0; } case Second s -> { numA = 0; numB = 1; } /* default -> { numA = 2; numB = 2; } */ } System.out.println(numA + ""); System.out.println(numB + ""); } I would like it if the compiler could make the same realizations it did for the if statement. I should also add, this seems to apply to all forms of exhaustive switch except for those with default clauses. Obviously, this is easy to work around - I could just make a switch expression for each variable, and then just populate them individually. Alternatively, I could just put a default clause any time I need that compiler realization, though I give up some benefits from exhaustive switch as a result. Thank you all for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Oct 8 10:22:45 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 8 Oct 2022 12:22:45 +0200 (CEST) Subject: Compiler could be smarter when dealing with exhaustive switch and final variables? In-Reply-To: References: Message-ID: <2006002408.21426909.1665224565950.JavaMail.zimbra@u-pem.fr> > From: "David Alayachew" > To: "amber-dev" > Sent: Saturday, October 8, 2022 9:07:31 AM > Subject: Compiler could be smarter when dealing with exhaustive switch and final > variables? > Hello Amber Dev Team, > When if/else statements and final variables combine, the compiler can make some > helpful assertions. Consider the following code. > sealed interface SomeInterface {} > record First() implements SomeInterface {} > record Second() implements SomeInterface {} > private void exhaustiveIfWithFinalUninitialized() > { > SomeInterface abc = new First(); > final int numA; > final int numB; > if (abc instanceof First) > { > numA = -1; > numB = -1; > } > else if (abc instanceof Second) > { > numA = -2; > numB = -2; > } > else > { > throw new IllegalStateException("This shouldn't be possible!"); > } > System.out.println(numA + ""); > System.out.println(numB + ""); > } > The above code compiles with no errors whatsoever. The compiler is smart enough > to realize that, no matter what, numA and numB will most certainly be populated > by the time we reach the print statements below the if statements. > However, the compiler does not make the same realization when working with > exhaustive switches. But since the switch statement is exhaustive, shouldn't > the compiler be able to make the same realization here? If anything, the switch > should be capable of all of the above and more, correct? > private void exhaustiveSwitchWithFinalUninitialized() > { > final int numA; > final int numB; > SomeInterface abc = new First(); > switch (abc) //compiler error unless we uncomment the default > { > case First f -> > { > numA = 1; > numB = 0; > } > case Second s -> > { > numA = 0; > numB = 1; > } > /* > default -> > { > numA = 2; > numB = 2; > } > */ > } > System.out.println(numA + ""); > System.out.println(numB + ""); > } > I would like it if the compiler could make the same realizations it did for the > if statement. I should also add, this seems to apply to all forms of exhaustive > switch except for those with default clauses. Hi David, i think this is similar to [ https://bugs.openjdk.org/browse/JDK-8294670 | https://bugs.openjdk.org/browse/JDK-8294670 ] that i've reported recently. > Obviously, this is easy to work around - I could just make a switch expression > for each variable, and then just populate them individually. Alternatively, I > could just put a default clause any time I need that compiler realization, > though I give up some benefits from exhaustive switch as a result. As a workaround, you can also group the values into a record (that can be local to the method) and use an expression switch and initialize the local variable after the switch. You can even abuse of the switch + record pattern to extract numA and numB :) record Pair(int numA, int numB) {} switch(switch(abc) { case First -> new Pair(1, 0); case Second -> new Pair(0, 1); }) { case Pair(int numA, int numB) -> { System.out.println(numA); System.out.println(numB); } } BTW, this kind of code is less unreadable in languages like Scala where the switch is postfix to the value switched upon and not prefix. > Thank you all for your time and help! > David Alayachew regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Oct 8 14:34:10 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 8 Oct 2022 10:34:10 -0400 Subject: Compiler could be smarter when dealing with exhaustive switch and final variables? In-Reply-To: <2006002408.21426909.1665224565950.JavaMail.zimbra@u-pem.fr> References: <2006002408.21426909.1665224565950.JavaMail.zimbra@u-pem.fr> Message-ID: Hello R?mi, First off, apologies for double sending - I sent to just you instead of to the mailing list too. > Hi David, > i think this is similar to > https://bugs.openjdk.org/browse/JDK-8294670 > that i've reported recently. Whoops, apologies. I even see linked in the bug report that the issue was even reported on the same mailing list barely a week ago. I've been falling behind on reading the mailing lists. I'll be sure to catch up before posting bug reports in the future. > You can even abuse of the switch + record pattern > to extract numA and numB :) What a clever idea lol. Expressiveness be damned, but your solution actually works. I have been trying to use local records more frequently, but there's this one annoying edge case that has made me shy away from using them as much ( https://bugs.openjdk.org/browse/JDK-8287885). But if there's flexibility like this to be found, then it sounds like I should lean more into local records. > BTW, this kind of code is less unreadable in languages like Scala where > the switch is postfix to the value switched upon and not prefix. I'm still digesting the comment about type classes that you mentioned last time. Seems like Scala has a lot to teach me as a language. Thank you for your response and its insight! David Alayachew On Sat, Oct 8, 2022 at 6:22 AM Remi Forax wrote: > > > ------------------------------ > > *From: *"David Alayachew" > *To: *"amber-dev" > *Sent: *Saturday, October 8, 2022 9:07:31 AM > *Subject: *Compiler could be smarter when dealing with exhaustive switch > and final variables? > > Hello Amber Dev Team, > > When if/else statements and final variables combine, the compiler can make > some helpful assertions. Consider the following code. > > sealed interface SomeInterface {} > record First() implements SomeInterface {} > record Second() implements SomeInterface {} > > private void exhaustiveIfWithFinalUninitialized() > { > > SomeInterface abc = new First(); > > final int numA; > final int numB; > > if (abc instanceof First) > { > > numA = -1; > numB = -1; > > } > > else if (abc instanceof Second) > { > > numA = -2; > numB = -2; > > } > > else > { > > throw new IllegalStateException("This shouldn't be possible!"); > > } > > System.out.println(numA + ""); > System.out.println(numB + ""); > > } > > The above code compiles with no errors whatsoever. The compiler is smart > enough to realize that, no matter what, numA and numB will most certainly > be populated by the time we reach the print statements below the if > statements. > > However, the compiler does not make the same realization when working with > exhaustive switches. But since the switch statement is exhaustive, > shouldn't the compiler be able to make the same realization here? If > anything, the switch should be capable of all of the above and more, > correct? > > private void exhaustiveSwitchWithFinalUninitialized() > { > > final int numA; > final int numB; > > SomeInterface abc = new First(); > > switch (abc) //compiler error unless we uncomment the default > { > > case First f -> > { > > numA = 1; > numB = 0; > > } > > case Second s -> > { > > numA = 0; > numB = 1; > > } > /* > default -> > { > > numA = 2; > numB = 2; > > } > */ > } > > System.out.println(numA + ""); > System.out.println(numB + ""); > > } > > I would like it if the compiler could make the same realizations it did > for the if statement. I should also add, this seems to apply to all forms > of exhaustive switch except for those with default clauses. > > > Hi David, > i think this is similar to > https://bugs.openjdk.org/browse/JDK-8294670 > that i've reported recently. > > > Obviously, this is easy to work around - I could just make a switch > expression for each variable, and then just populate them individually. > Alternatively, I could just put a default clause any time I need that > compiler realization, though I give up some benefits from exhaustive switch > as a result. > > > As a workaround, you can also group the values into a record (that can be > local to the method) and use an expression switch and initialize the local > variable after the switch. > > You can even abuse of the switch + record pattern to extract numA and numB > :) > > record Pair(int numA, int numB) {} > switch(switch(abc) { > case First -> new Pair(1, 0); > case Second -> new Pair(0, 1); > }) { > case Pair(int numA, int numB) -> { > System.out.println(numA); > System.out.println(numB); > } > } > > BTW, this kind of code is less unreadable in languages like Scala where > the switch is postfix to the value switched upon and not prefix. > > Thank you all for your time and help! > David Alayachew > > > regards, > R?mi > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From jan.lahoda at oracle.com Sat Oct 8 16:47:39 2022 From: jan.lahoda at oracle.com (Jan Lahoda) Date: Sat, 8 Oct 2022 16:47:39 +0000 Subject: Compiler could be smarter when dealing with exhaustive switch and final variables? In-Reply-To: References: <2006002408.21426909.1665224565950.JavaMail.zimbra@u-pem.fr> Message-ID: Hi David, Would you please have an exact reproducible testcase and the exact error that is produced? Based on your e-mail, I tried: --- class Test { sealed interface SomeInterface {} record First() implements SomeInterface {} record Second() implements SomeInterface {} private void exhaustiveSwitchWithFinalUninitialized() { final int numA; final int numB; SomeInterface abc = new First(); switch (abc) //compiler error unless we uncomment the default { case First f -> { numA = 1; numB = 0; } case Second s -> { numA = 0; numB = 1; } /* default -> { numA = 2; numB = 2; } */ } System.out.println(numA + ""); System.out.println(numB + ""); } } --- Which seems to build fine on a (custom build of) JDK 19b36 (jdk-19+36): --- $ javac --enable-preview -source 19 /tmp/Test.java Note: /tmp/Test.java uses preview features of Java SE 19. Note: Recompile with -Xlint:preview for details. --- I checked both the JLS and javac, at it seems that as long as the switch is exhaustive, the variables should be definitely assigned. Thanks, Jan ________________________________ From: amber-dev on behalf of David Alayachew Sent: Saturday, October 8, 2022 4:34 PM To: Remi Forax Cc: amber-dev Subject: Re: Compiler could be smarter when dealing with exhaustive switch and final variables? Hello R?mi, First off, apologies for double sending - I sent to just you instead of to the mailing list too. > Hi David, > i think this is similar to > https://bugs.openjdk.org/browse/JDK-8294670 > that i've reported recently. Whoops, apologies. I even see linked in the bug report that the issue was even reported on the same mailing list barely a week ago. I've been falling behind on reading the mailing lists. I'll be sure to catch up before posting bug reports in the future. > You can even abuse of the switch + record pattern > to extract numA and numB :) What a clever idea lol. Expressiveness be damned, but your solution actually works. I have been trying to use local records more frequently, but there's this one annoying edge case that has made me shy away from using them as much (https://bugs.openjdk.org/browse/JDK-8287885). But if there's flexibility like this to be found, then it sounds like I should lean more into local records. > BTW, this kind of code is less unreadable in languages like Scala where > the switch is postfix to the value switched upon and not prefix. I'm still digesting the comment about type classes that you mentioned last time. Seems like Scala has a lot to teach me as a language. Thank you for your response and its insight! David Alayachew On Sat, Oct 8, 2022 at 6:22 AM Remi Forax > wrote: ________________________________ From: "David Alayachew" > To: "amber-dev" > Sent: Saturday, October 8, 2022 9:07:31 AM Subject: Compiler could be smarter when dealing with exhaustive switch and final variables? Hello Amber Dev Team, When if/else statements and final variables combine, the compiler can make some helpful assertions. Consider the following code. sealed interface SomeInterface {} record First() implements SomeInterface {} record Second() implements SomeInterface {} private void exhaustiveIfWithFinalUninitialized() { SomeInterface abc = new First(); final int numA; final int numB; if (abc instanceof First) { numA = -1; numB = -1; } else if (abc instanceof Second) { numA = -2; numB = -2; } else { throw new IllegalStateException("This shouldn't be possible!"); } System.out.println(numA + ""); System.out.println(numB + ""); } The above code compiles with no errors whatsoever. The compiler is smart enough to realize that, no matter what, numA and numB will most certainly be populated by the time we reach the print statements below the if statements. However, the compiler does not make the same realization when working with exhaustive switches. But since the switch statement is exhaustive, shouldn't the compiler be able to make the same realization here? If anything, the switch should be capable of all of the above and more, correct? private void exhaustiveSwitchWithFinalUninitialized() { final int numA; final int numB; SomeInterface abc = new First(); switch (abc) //compiler error unless we uncomment the default { case First f -> { numA = 1; numB = 0; } case Second s -> { numA = 0; numB = 1; } /* default -> { numA = 2; numB = 2; } */ } System.out.println(numA + ""); System.out.println(numB + ""); } I would like it if the compiler could make the same realizations it did for the if statement. I should also add, this seems to apply to all forms of exhaustive switch except for those with default clauses. Hi David, i think this is similar to https://bugs.openjdk.org/browse/JDK-8294670 that i've reported recently. Obviously, this is easy to work around - I could just make a switch expression for each variable, and then just populate them individually. Alternatively, I could just put a default clause any time I need that compiler realization, though I give up some benefits from exhaustive switch as a result. As a workaround, you can also group the values into a record (that can be local to the method) and use an expression switch and initialize the local variable after the switch. You can even abuse of the switch + record pattern to extract numA and numB :) record Pair(int numA, int numB) {} switch(switch(abc) { case First -> new Pair(1, 0); case Second -> new Pair(0, 1); }) { case Pair(int numA, int numB) -> { System.out.println(numA); System.out.println(numB); } } BTW, this kind of code is less unreadable in languages like Scala where the switch is postfix to the value switched upon and not prefix. Thank you all for your time and help! David Alayachew regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Oct 8 19:35:55 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 8 Oct 2022 15:35:55 -0400 Subject: Compiler could be smarter when dealing with exhaustive switch and final variables? In-Reply-To: References: <2006002408.21426909.1665224565950.JavaMail.zimbra@u-pem.fr> Message-ID: Terribly sorry Jan, I have been using JDK 18.0.2 this entire time. For JDK 19, everything works, as you said. However, for JDK 18.0.2, this is my repeatable example and the corresponding error. public class TestExhaustivenessBug { sealed interface SomeInterface {} record First() implements SomeInterface {} record Second() implements SomeInterface {} private void exhaustiveIfWithFinalUninitialized() { SomeInterface abc = new First(); final int numA; final int numB; if (abc instanceof First) { numA = -1; numB = -1; } else if (abc instanceof Second) { numA = -2; numB = -2; } else { throw new IllegalStateException("This shouldn't be possible!"); } System.out.println(numA + ""); System.out.println(numB + ""); } private void exhaustiveSwitchWithFinalUninitialized() { final int numA; final int numB; SomeInterface abc = new First(); switch (abc) { case First f -> { numA = 1; numB = 0; } case Second s -> { numA = 0; numB = 1; } } System.out.println(numA + ""); System.out.println(numB + ""); } } And here is the error I received. TestExhaustivenessBug.java:74: error: variable numA might not have been initialized System.out.println(numA + ""); ^ TestExhaustivenessBug.java:75: error: variable numB might not have been initialized System.out.println(numB + ""); ^ Note: TestExhaustivenessBug.java uses preview features of Java SE 18. Note: Recompile with -Xlint:preview for details. 2 errors Apologies for the trouble and confusion. -------------- next part -------------- An HTML attachment was scrubbed... URL: From jlahoda at openjdk.org Tue Oct 11 13:41:14 2022 From: jlahoda at openjdk.org (Jan Lahoda) Date: Tue, 11 Oct 2022 13:41:14 GMT Subject: git: openjdk/amber: created branch for-patterns based on the branch templated-strings containing 326 unique commits Message-ID: <0b8589f6-b24b-439d-9ac5-d6ebaf5440ca@openjdk.org> The following commits are unique to the for-patterns branch: ======================================================== 43e191d6: 8293524: RISC-V: Use macro-assembler functions as appropriate 812d805a: 6447816: Provider filtering (getProviders) is not working with OR'd conditions 9d6b0285: 8234315: GTK LAF does not gray out disabled JMenu 3dd94f33: 8292671: Hotspot Style Guide should allow covariant returns 7169ee5c: 8293477: IGV: Upgrade to Netbeans Platform 15 00befddd: 8292675: Add identity transformation for removing redundant AndV/OrV nodes a14c3a49: 8288933: Improve the implementation of Double/Float.isInfinite b8598b02: 8291660: Grapheme support in BreakIterator dbec22b8: 8293287: add ReplayReduce flag 45ff10cc: 8292695: SIGQUIT and jcmd attaching mechanism does not work with signal chaining library 91d00b30: 8288473: Remove unused frame::set_pc_preserve_deopt methods 005b49bb: 8293044: C1: Missing access check on non-accessible class 68da02c7: 8292240: CarrierThread.blocking not reset when spare not activated 699c4296: 8292866: Java_sun_awt_shell_Win32ShellFolder2_getLinkLocation check MultiByteToWideChar return value for failures 37df5f56: 8291599: Assertion in PhaseIdealLoop::skeleton_predicate_has_opaque after JDK-8289127 d5aae010: 8293544: G1: Add comment in G1BarrierSetC1::pre_barrier 4c77bd32: 6529151: NullPointerException in swing.plaf.synth.SynthLookAndFeel$Handler 91c9091d: 8293343: sun/management/jmxremote/bootstrap/RmiSslNoKeyStoreTest.java failed with "Agent communication error: java.io.EOFException" 0c61bf10: 8293282: LoadLibraryUnloadTest.java fails with "Too few cleared WeakReferences" 9ef6c092: 8287908: Use non-cloning reflection methods where acceptable cea409cc: 8292738: JInternalFrame backgroundShadowBorder & foregroundShadowBorder line is longer in Mac Look and Feel 524af949: 8283627: Outdated comment in MachineDescriptionTwosComplement.isLP64 d3f7e3b4: 8293339: vm/jvmti/StopThread/stop001/stop00103 crashes with SIGSEGV in Continuation::is_continuation_mounted 155b10ae: 8293329: x86: Improve handling of constants in AES/GHASH stubs 41ce6582: 8292225: Rename ArchiveBuilder APIs related to source and buffered addresses 526eb54f: 8293669: SA: Remove unnecssary "InstanceStackChunkKlass: InstanceStackChunkKlass" output when scanning heap 68645ebf: 8293566: RISC-V: Clean up push and pop registers cbee0bc9: 8292587: AArch64: Support SVE fabd instruction ec2629c0: 8275275: AArch64: Fix performance regression after auto-vectorization on NEON 9cd3e355: 4834298: JFileChooser.getSelectedFiles() failed with multi-selection and double-click 8bd79d3e: 8170305: URLConnection doesn't handle HTTP/1.1 1xx (informational) messages dfc16e04: 8292302: Windows GetLastError value overwritten by ThreadLocalStorage::thread 1e1db5de: 8292591: Experimentally add back barrier-less Java thread transitions 37234c85: 8291912: Introduce per-allocation target struct for members in G1PLABAllocator 725f41ff: 8293351: Add second tmp register to aarch64 BarrierSetAssembler::load_at 6f2223fa: 8288180: C2: VectorPhase must ensure that SafePointNode memory input is a MergeMemNode 1dc5039f: 8293626: AccessFlag::locations(ClassFileFormatVersion cffv) does not throw NPEx when parameter is null 90390222: 8287394: AArch64: Remove cbuf parameter from far_call/far_jump/trampoline_call 7e020398: 8293647: Avoid unnecessary boxing in jdk.hotspot.agent b3461c18: 8293680: PPC64BE build failure after JDK-8293344 60f59a4a: 8293660: Fix frame::sender_for_compiled_frame frame size assert 2baf2516: 8293654: Improve SharedRuntime handling of continuation helper out-arguments 1169a159: 8291657: Javac assertion when compiling a method call with switch expression as argument 91f9c0d0: 8293774: Improve TraceOptoParse to dump the bytecode name 8351b30e: 8293771: runtime/handshake/SystemMembarHandshakeTransitionTest.java fails if MEMBARRIER_CMD_QUERY is unsupported 2a387918: 8292755: Non-default method in interface leads to a stack overflow in JShell 7f3250d7: 8293787: Linux aarch64 build fails after 8292591 211fab8d: 8291669: [REDO] Fix array range check hoisting for some scaled loop iv 95c7c556: 8293402: hs-err file printer should reattempt stack trace printing if it fails a75ddb83: 8293122: (fs) Use file cloning in macOS version of Files::copy method d191e475: 8293768: Add links to JLS 19 and 20 from SourceVersion enum constants 7376c552: 8293769: RISC-V: Add a second temporary register for BarrierSetAssembler::load_at 2057070e: 8293815: P11PSSSignature.engineUpdate should not print debug messages during normal operation eeb625e7: 8290169: adlc: Improve child constraints for vector unary operations 1caba0f1: 8292948: JEditorPane ignores font-size styles in external linked css-file 8f3bbe95: 8293472: Incorrect container resource limit detection if manual cgroup fs mounts present b31a03c6: 8293695: Implement isInfinite intrinsic for RISC-V 15cb1fb7: 8256265: G1: Improve parallelism in regions that failed evacuation aff5ff14: 8244681: Add a warning for possibly lossy conversion in compound assignments fbd8b42d: 8293591: Remove use of Thread.stop from jshell tests 6fca9ae0: 8288474: Move EventContinuationFreezeOld from try_freeze_fast to freeze_slow ecb456ae: 8293779: redundant checking in AESCrypt.makeSessionKey() method 2028ec74: 8289608: Change com/sun/jdi tests to not use Thread.suspend/resume bf79f99c: 8292989: Avoid dynamic memory in AsyncLogWriter 6beeb847: 8293875: ProblemList sun/management/jmxremote/bootstrap/RmiBootstrapTest.java#id1 on linux-x64 4cec141a: 8291509: Minor cleanup could be done in sun.security bb9aa4ea: 8293813: ProblemList com/sun/jdi/JdbLastErrorTest.java on windows-x64 in Xcomp mode 9a40b76a: 8293842: IPv6-only systems throws UnsupportedOperationException for several socket/TCP options 3beca2db: 8291600: [vectorapi] vector cast op check is not always needed for vector mask cast 141d5f5d: 8293767: AWT test TestSinhalaChar.java has old SCCS markings 11e7d53b: 8293819: sun/util/logging/PlatformLoggerTest.java failed with "RuntimeException: Retrieved backing PlatformLogger level null is not the expected CONFIG" 7765942a: 8290367: Update default value and extend the scope of com.sun.jndi.ldap.object.trustSerialData system property 39cd1635: 8293578: Duplicate ldc generated by javac 5feca688: 8293840: RISC-V: Remove cbuf parameter from far_call/far_jump/trampoline_call f42caefe: 8293550: Optionally add get-task-allow entitlement to macos binaries dfb9c066: 8293535: jdk/javadoc/doclet/testJavaFX/TestJavaFxMode.java fail with jfx 4b297c1c: 8293892: Add links to JVMS 19 and 20 from ClassFileFormatVersion enum constants 746f5f58: 8293816: CI: ciBytecodeStream::get_klass() is not consistent a8f0f575: 8278165: Clarify that ZipInputStream does not access the CEN fields for a ZipEntry 4b8399b5: 8293251: Use stringStream::base() instead of as_string() when applicable 01e7b881: 8290917: x86: Memory-operand arithmetic instructions have too low costs cfd44bb2: 8293218: serviceability/tmtools/jstat/GcNewTest.java fails with "Error in the percent calculation" b6ff8fa3: 8292073: NMT: remove unused constructor parameter from MallocHeader b1ed40a8: 8293466: libjsig should ignore non-modifying sigaction calls 357a2cc2: 8293937: x86: Drop LP64 conditions from clearly x86_32 code 26e08cf3: 8293844: C2: Verify Location::{oop,normal} types in PhaseOutput::FillLocArray 43f7f47a: 8293499: Provide jmod --compress option d7c1a763: 8293861: G1: Disable preventive GCs by default d77c464c: 8293891: gc/g1/mixedgc/TestOldGenCollectionUsage.java (still) assumes that GCs take 1ms minimum 04d7b7d5: 8293503: gc/metaspace/TestMetaspacePerfCounters.java#Epsilon-64 failed assertGreaterThanOrEqual: expected MMM >= NNN cbd0688b: 8293851: hs_err should print more stack in hex dump 36c9034f: 8293808: mscapi destroyKeyContainer enhance KeyStoreException: Access is denied exception a93cf926: 8293920: G1: Add index based heap region iteration 471e2f12: 8292088: C2: assert(is_OuterStripMinedLoop()) failed: invalid node class: IfTrue d41f69f9: 8293849: PrintIdealPhase in compiler directives file is ignored when used with other compile commands ab7f58a3: 6286501: JTabbedPane throws NPE from its stateChanged listener in particular case 5725a93c: 8293879: Remove unnecessary castings in jdk.hotspot.agent b920d299: 8271328: User is able to choose the color after disabling the color chooser. 8082c24a: 8054572: [macosx] JComboBox paints the border incorrectly f91762f5: 8293965: Code signing warnings after JDK-8293550 64b96e5c: 8293811: Provide a reason for PassFailJFrame.forceFail 1f9ff413: 8292297: Fix up loading of override java.security properties file 6e23b432: 8293502: (fc) FileChannel::transfer methods fail to copy /proc files on Linux 8ff2c263: 8293942: [JVMCI] data section entries must be 4-byte aligned on AArch64 4020ed53: 8293210: G1: Remove redundant check in G1FreeHumongousRegionClosure 0fa7d9e8: 8278863: Add method ClassDesc::ofInternalName fe541f05: 8293989: [JVMCI] re-use cleared oop handles a07902bc: 8293976: Use unsigned integers in Assembler/CodeBuffer::emit_int* 1b496064: 8293922: Extend barrier-less Java thread transitions to native transitions 84ee1a29: 8293781: RISC-V: Clarify types of calls 0f28cb06: 8294014: Remove redundant UseCompiler conditions bb422f5c: 8293595: tstrings::any() is missing an overload 584de68d: 8294058: Early use of lambda introduced in JDK-8285263 cause startup regressions in 20-b02 09af637a: 8294012: RISC-V: get/put_native_u8 missing the case when address&7 is 6 df8ec09f: 8294046: Newly added test test/jdk/javax/swing/JTabbedPane/TestNPEStateChgListener.java fails in macos e3358e77: 8294008: Grapheme implementation of setText() throws IndexOutOfBoundsException 5002eaa5: 8293828: JFR: jfr/event/oldobject/TestClassLoaderLeak.java still fails when GC cycles are not happening cb5771dc: 8294006: Avoid hardcoding object file suffixes in make caae53f4: 8289508: Improve test coverage for XPath Axes: ancestor, ancestor-or-self, preceding, and preceding-sibling 3b438a68: 8294067: [macOS] javax/swing/JComboBox/6559152/bug6559152.java Cannot select an item from popup with the ENTER key. 21008cad: 8285383: vmTestbase/nsk/jvmti/scenarios/hotswap/HS204/hs204t001/hs204t001.java failed with "exit code: 96" cddd6def: 8279941: sun/security/pkcs11/Signature/TestDSAKeyLength.java fails when NSS version detection fails cb72f809: 8293978: Duplicate simple loop back-edge will crash the vm 12e3510f: 8293798: Fix test bugs due to incompatibility with -XX:+AlwaysIncrementalInline 379f3094: 8287217: C2: PhaseCCP: remove not visited nodes, prevent type inconsistency 8ecdaa68: 8294000: Filler array klass should be in jdk/vm/internal, not in java/vm/internal 4e7cb156: 8293480: IGV: Update Bytecode and ControlFlow Component immediately when opening a new graph 8d1dd6a6: 8294076: Improve ant detection in idea.sh 844a95b9: 8292892: Javadoc index descriptions are not deterministic e9401e67: 8293364: IGV: Refactor Action in EditorTopComponent and fix minor bugs cd1cdcdb: 8293116: Incremental JDK build could be sped up da4fdfbb: 8293659: Improve UnsatisfiedLinkError error message to include dlopen error details d14e96d9: 8293493: Signal Handlers printout should show signal block state 95ec2eac: 8293897: Synthetic final modifier is part of the AST for a try-with-resource resource 0746bcb6: 8294083: RISC-V: Minimal build failed with --disable-precompiled-headers 07afa3f4: 8294110: compiler/uncommontrap/Decompile.java fails after JDK-8293798 84d7ff64: 8288129: Shenandoah: Skynet test crashed with iu + aggressive e1958971: 8294068: Unconditional and eager load of nio library since JDK-8264744 27b8e2f4: 8294038: Remove "Classpath" exception from javax/swing tests 711e2520: 8294039: Remove "Classpath" exception from java/awt tests c6be2cd3: 8293156: Dcmd VM.classloaders fails to print the full hierarchy 9f90eb05: 8294062: Improve parsing performance of j.l.c.MethodTypeDesc 2283c324: 8294149: JMH 1.34 and later requires jopt-simple 5.0.4 742bc041: 8294100: RISC-V: Move rt_call and xxx_move from SharedRuntime to MacroAssembler 47f233ac: 8292202: modules_do is called without Module_lock d5bee4a0: 8294086: RISC-V: Cleanup InstructionMark usages in the backend 03f287d4: 8293995: Problem list sun/tools/jstatd/TestJstatdRmiPort.java on all platforms because of 8293577 5652030f: 8292376: A few Swing methods use inheritDoc on exceptions which are not inherited 83abfa5d: 8255670: Improve C2's detection of modified nodes 800e68d6: 8292044: HttpClient doesn't handle 102 or 103 properly 3fa6778a: 8292296: Use multiple threads to process ParallelGC deferred updates a216960d: 8294087: RISC-V: RVC: Fix a potential alignment issue and add more alignment assertions for the patchable calls/nops d781ab09: 8294003: Don't handle si_addr == 0 && si_code == SI_KERNEL SIGSEGVs bc2af47e: 8254711: Add java.security.Provider.getService JFR Event 0be2b2c2: 8292756: java.lang.AssertionError at at jdk.compiler/com.sun.tools.javac.code.Scope$ScopeImpl.leave(Scope.java:386) 4a6060ba: 8294190: Incorrect check messages in SharedRuntime::generate_uncommon_trap_blob f751e608: 8294197: Zero: JVM_handle_linux_signal should not assume deopt NOPs 48cc1560: 8293331: Refactor FileDispatcherImpl into operating system-specific components 696287d6: 8294037: Using alias template to unify hashtables in AsyncLogWriter 5285035e: 8294075: gtest/AsyncLogGtest crashes with SEGV df53fa75: 8292328: AccessibleActionsTest.java test instruction for show popup on JLabel did not specify shift key f3ba332f: 8294183: AArch64: Wrong macro check in SharedRuntime::generate_deopt_blob a4dc035a: 8290910: Wrong memory state is picked in SuperWord::co_locate_pack() f6d78cda: 8293657: sun/management/jmxremote/bootstrap/RmiBootstrapTest.java#id1 failed with "SSLHandshakeException: Remote host terminated the handshake" eec992c6: 8292602: ZGC: C2 late barrier analysis uses invalid dominator information 05c8cabd: 8293532: Use lighter jmod compression levels in build config acd5bcfc: 8289610: Degrade Thread.stop 0b56b822: 8293991: java/lang/Float/Binary16ConversionNaN.java fails on silent NaN conversions acd75e0f: 8294053: Unneeded local variable in handle_safefetch() 664e5b1d: 8294187: RISC-V: Unify all relocations for the backend into AbstractAssembler::relocate() e45f3d51: 8294281: Allow warnings to be disabled on a per-file basis 2e20e7ec: 8294271: Remove use of ThreadDeath from make utilities eca9749d: 8288325: [windows] Actual and Preferred Size of AWT Non-resizable frame are different 6ecd0817: 8294270: make test passes awkward -status:-status:error,fail to jtreg e2f82514: 8293618: x86: Wrong code generation in class Assembler 543851db: 8289607: Change hotspot/jtreg tests to not use Thread.suspend/resume 3675f4c2: 8293252: Shenandoah: ThreadMXBean synchronizer tests crash with aggressive heuristics 169a5d48: 8294193: Files.createDirectories throws FileAlreadyExistsException for a symbolic link whose target is an existing directory 91a23d77: 8294142: make test should report only on executed tests 050eebf2: 8294245: Make Compile::print_inlining_stream stack allocated 2be31587: 4797982: Setting negative size of JSplitPane divider leads to unexpected results. 36b61c5d: 8293872: Make runtime/Thread/ThreadCountLimit.java more robust 968af74d: 8293567: AbstractSplittableWithBrineGenerator: salt has digits that duplicate the marker 5ae6bc23: 8234262: Unmask SIGQUIT in a child process 1f521a12: 8225012: sanity/client/SwingSet/src/ToolTipDemoTest.java fails on Windows aca4276e: 8294379: Missing comma after copyright year b88ee1ee: 6251738: Want a top-level summary page that itemizes all spec documents referenced from javadocs (OEM spec) 43eff2b3: 8272687: Replace StringBuffer with StringBuilder in RuleBasedCollator 1e222bcc: 8293462: [macos] app image signature invalid when creating DMG or PKG from post processed signed image 1ddc92fe: 8294404: [BACKOUT] JDK-8294142: make test should report only executed tests aa48705d: 8289422: Fix and re-enable vector conditional move 49a7347b: 8294408: Problemlist runtime/handshake/HandshakeSuspendExitTest.java 02ea3381: 8293887: AArch64 build failure with GCC 12 due to maybe-uninitialized warning in libfdlibm k_rem_pio2.c 14c6ac45: 8293998: [PPC64] JfrGetCallTrace: assert(_pc != nullptr) failed: must have PC dd51f7e0: 8293996: C2: fix and simplify IdealLoopTree::do_remove_empty_loop bc12e955: 8292969: Bad Thread Utilization in ForkJoinPool 1abf971b: 8249627: Degrade Thread.suspend and Thread.resume 3419363e: 8294361: Cleanup usages of StringBuffer in SQLOutputImpl 99017b06: 8293064: Remove unused NET_xxx functions 112ca2b8: 8293964: Unused check_for_duplicates parameter in ClassLoaderExt::process_jar_manifest fb4979c0: 8290401: Support dump all phases and print nodes in ascending order of index 71511283: 8294317: Insufficient build rules for tzdb.dat a11477ce: 8289797: tools/launcher/I18NArgTest.java fails on Japanese Windows environment 739fdec7: 8289162: runtime/NMT/ThreadedMallocTestType.java should print out memory allocations to help debug 763d4bf0: 8293592: Remove JVM_StopThread, stillborn, and related cleanup 22b59b66: 8294471: SpecTaglet is inconsistent with SpecTree for inline property 6ad151d0: 8293143: Workaround for JDK-8292217 when doing "step over" of bytecode with unresolved cp reference f8d9fa88: 8294483: Remove vmTestbase/nsk/jvmti/GetThreadState tests. e5b65c40: 8290482: Update JNI Specification of DestroyJavaVM for better alignment with JLS, JVMS, and Java SE API Specifications 9d76ac8a: 8292158: AES-CTR cipher state corruption with AVX-512 d827fd83: 8294430: RISC-V: Small refactoring for movptr_with_offset 5e1e449c: 8290920: sspi_bridge.dll not built if BUILD_CRYPTO is false 79ccc791: 8293613: need to properly handle and hide tmp VTMS transitions c13e0ef3: 8292848: AWT_Mixing and TrayIcon tests fail on el8 with hard-coded isOel7 1ea0d6b4: 8292301: [REDO v2] C2 crash when allocating array of size too large 94e14da0: 8294057: Parallel: Tighten ParallelCompactData::initialize_region_data c42ef70a: 7148092: [macosx] When Alt+down arrow key is pressed, the combobox popup does not appear. ea616710: 8294359: Interpreter(AArch64) intrinsify Thread.currentThread() 60616f24: 8294059: Serial: Refactor GenCollectedHeap::collect 37f83b9b: 8294375: test/jdk/java/nio/channels/vthread/BlockingChannelOps.java is slow 30e3bf9d: 8291805: IGV: Improve Zooming 70d8428e: 8294520: Problemlist java/nio/file/Files/CopyProcFile.java 7401fe04: 8292912: Make guard card in CardTable inaccessible 7515b304: 8279283: BufferedInputStream should override transferTo 4fb424ba: 8293961: Unused ClassPathZipEntry::contents_do 3b7fc80b: 8294411: SA should provide more useful info when it fails to start up due to "failed to workaround classshareing" 9309786d: 8294472: Remove redundant rawtypes suppression in AbstractChronology 9db95edd: 8215788: Clarify JarInputStream Manifest access 76f18651: 8293563: [macos-aarch64] SA core file tests failing with sun.jvm.hotspot.oops.UnknownOopException 88731924: 8293515: heapShared.cpp: rename JavaThread parameter to current 6f8f28e7: 8294160: misc crash dump improvements 8491fd5c: 8294551: Put java/io/BufferedInputStream/TransferTo.java on problem list 1decdcee: 8294492: RISC-V: Use li instead of patchable movptr at non-patchable callsites ce85cac9: 8065554: MatchResult should provide values of named-capturing groups 5d48da45: 8294370: Fix allocation bug in java_lang_Thread::async_get_stack_trace() 29c70f1a: 8294595: Add javax/swing/plaf/aqua/CustomComboBoxFocusTest.java to problem list 545ded1a: 8294548: Problem list SA core file tests on macosx-x64 due to JDK-8294316 5f6ad926: 8294547: HotSpotAgent.setupVM() should include "cause" exception when throwing DebuggerException aeef3ecd: 8294198: Implement isFinite intrinsic for RISC-V 88062eea: 8293969: breakup the long signature in SystemDictionaryShared::is_supported_invokedynamic a07975bf: 8294519: (fs) java/nio/file/Files/CopyProcFile.java fails intermittenly due to unstable /proc/cpuinfo output b030c7de: 8225235: Unused field defaultIndex in NetworkInterface 9dce8652: 8294115: JNI local refs exceeds capacity warning in NetworkInterface::getAll 7c60e6d2: 8293770: RISC-V: Reuse runtime call trampolines 81fda1b7: 8294569: Remove CardTable::_last_valid_index 69749788: 8294521: Parallel: Remove unused field in UpdateDensePrefixAndCompactionTask 6d83482a: 8293540: [Metrics] Incorrectly detected resource limits with additional cgroup fs mounts b4e1e416: 8293000: Review running times of jshell regression tests 64da8620: 8287597: List all preview features on the javadoc PREVIEW page f0157336: 8294357: (tz) Update Timezone Data to 2022d c2ce43cb: 8291021: JFR: Only one finished state in ChunkHeader class 052a9249: 8294293: Remove unused _width and _newlines field in outputStream 3efbd5f0: 8294626: Improve URL protocol lower casing 1d26c4b1: 8291022: JFR: Reduce logging in ChunkHeader constructor b8f9a915: 8293940: Some tests for virtual threads take too long 3b1bc217: 8294307: ISO 4217 Amendment 173 Update 48674d4f: 8291428: JFR: 'jfr print' displays incorrect timestamps during DST da4e96d5: 8276545: Fix handling of trap count overflow in Parse::Parse() d207da8a: 8294533: Documentation mistake in Process::getErrorStream and getInputStream 375f02fb: 8294608: Remove redundant unchecked suppression in FileDescriptor c7ab1caa: 8294609: C2: Improve inlining of methods with unloaded signature classes fd594302: 8294610: java/net/vthread/HttpALot.java is slow on Linux b8b9b97a: 8294676: [JVMCI] InstalledCode.deoptimize(false) should not touch address field a69ee853: 8292336: JFR: Warn users if -XX:StartFlightRecording:disk=false is specified with maxage or maxsize 03f25a9c: 8293562: blocked threads with KeepAliveCache.get bc668b99: 8293099: JFR: Typo in TestRemoteDump.java 6e8f0387: 8294567: IGV: IllegalStateException in search 8e9cfeb1: 8294431: jshell reports error on initialisation of static final field of anonymous class 5fe837a3: 8294236: [IR Framework] CPU preconditions are overriden by regular preconditions 08a7ecf4: 8294671: Remove unused CardValues::last_card ccc1d316: 8294529: IGV: Highlight the current graphs in the Outline f2a32d99: 8293691: converting a defined BasicType value to a string should not crash the VM 46633e64: 8294698: Remove unused 'checkedExceptions' param from MethodAccessorGenerator.generateMethod() edfb18a7: 8294695: Remove redundant deprecation suppression in ThreadGroup 4f44fd63: 8237467: jlink plugin to save the argument files as input to jlink in the output image e137f9f2: 8293877: Rewrite MineField test c6e3daa5: 8242115: C2 SATB barriers are not safepoint-safe a4f2078b: 8294437: java/nio/channels/FileChannel tests slow on Windows 081691ad: 8294593: Check the size of the target on invocations of BigInteger::isProbablePrime ed40d88f: 8276849: Refresh the window icon on graphics configuration changes 090cdfc7: 8294726: Update URLs in minefield tests 07ed68ef: 8288907: serviceability/jvmti/vthread/SuspendResume1/SuspendResume1.java fails with -XX:TieredStopAtLevel=2,3 ae79af2a: 8294740: Add cgroups keyword to TestDockerBasic.java f957ce99: 8294564: IGV: IllegalArgumentException for "Difference to current graph" bf39b184: 8288302: Shenandoah: SIGSEGV in vm maybe related to jit compiling xerces 16047e83: 8292780: misc tests failed "assert(false) failed: graph should be schedulable" 3b476a17: 8292847: Zero: Allow ergonomics to select the GC f03934e2: 8294578: [PPC64] C2: Missing is_oop information when using disjoint compressed oops mode 5a9cd336: 8294509: The sign extension bug applies to 'public static int[] convertSeedBytesToInts(byte[] seed, int n, int z)' in RandomSupport 4d6668e7: 8294242: JFR: jfr print doesn't handle infinite duration well 2dbedf0e: 8294406: Test runtime/handshake/HandshakeDirectTest.java failed: JVMTI_ERROR_WRONG_PHASE b850f052: 8294758: JFR: Docs build fails after changes to RecordedObject and Timespan 085949a1: 8294712: G1: Use index-base iteration for G1FlushHumongousCandidateRemSets 3644e26c: 8294673: JFR: Add SecurityProviderService#threshold to TestActiveSettingEvent.java 1166a8ab: 8292214: Memory leak in getAllConfigs of awt_GraphicsEnv.c:386 121d4a51: 8293579: tools/jpackage/share/jdk/jpackage/tests/UnicodeArgsTest.java fails on Japanese Windows platform b22a38de: 8292309: Fix java/awt/PrintJob/ConstrainedPrintingTest/ConstrainedPrintingTest.java test b2e86a62: 8294255: Add link to DEFAULT_WAIT_TIME in javadoc for SunToolKit.realsSync 1dafbe3f: 8294539: Augment discussion of equivalence relations on floating-point values 755958e5: 8294376: Minimize disabled warnings in java.base 43dbf589: 8186765: Speed up test sun/net/www/protocol/https/HttpsClient/ProxyAuthTest.java be82cff6: 8294748: Cleanup unneeded references to hg 953ce8da: 8293701: jdeps InverseDepsAnalyzer runs into NoSuchElementException: No value present b4e74aea: 8294514: Wrong initialization of nmethod::_consts_offset for native nmethods 4bdd1c91: 8290964: C2 compilation fails with assert "non-reduction loop contains reduction nodes" 8ebebbce: 8294368: Java incremental builds broken on Windows after JDK-8293116 13a5000d: 8294151: JFR: Unclear exception message when dumping stopped in memory recording b9eeec2b: 8294310: compare.sh fails on macos after JDK-8293550 979efd41: 8289004: investigate if SharedRuntime::get_java_tid parameter should be a JavaThread* bd90c4cf: 8282900: runtime/stringtable/StringTableCleaningTest.java verify unavailable at this moment ee6c3917: 8289925: Shared code shouldn't reference the platform specific method frame::interpreter_frame_last_sp() f531dae4: 8294840: langtools OptionalDependencyTest.java use File.pathSeparator 536c9a51: 8294618: Update openjdk.java.net => openjdk.org f2c57186: 8294734: Redundant override in AES implementation 87acfee3: 8294397: Replace StringBuffer with StringBuilder within java.text 0ec18382: 8294869: Correct failure of RemovedJDKInternals.java after JDK-8294618 d4142d84: 8290036: Define and specify Runtime shutdown sequence e986a97a: 8292330: Update JCov version to 3.0.13 8f561159: 8294679: RISC-V: Misc crash dump improvements 6029120a: 8278086: [REDO] ImageIO.write() method will throw IndexOutOfBoundsException 8c15f77a: 8270915: GIFImageReader disregards ignoreMetadata flag which causes memory exhaustion 7012d4ba: 8294837: unify Windows 2019 version check in os_windows and java_props_md e38ae8a6: 8294759: Print actual lock/monitor ranking ad7b7d40: 8294697: java/lang/Thread/virtual/ThreadAPI.testGetStackTrace2 failed with non-empty stack trace b00c64f0: 8294944: Implement record pattern improvements for Record Patterns (Second Preview) cf530cad: Adding @since, adding PreviewFeature, restoring API method. From amaembo at gmail.com Tue Oct 11 14:01:30 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 11 Oct 2022 16:01:30 +0200 Subject: [guards] Inconsistency when compiling guard with mutable local Message-ID: Hello! Consider the following code class Test { void test(Object o) { int s = 0; switch (new Object()) { case Integer i when (s = 1) > 0 -> {} default -> {} } } } With --enable-preview it's compiled successfully (tried 20-ea+18-1281) If we change (s = 1) to (s++), it cannot be compiled anymore with quite expected error message: Test.java:5: error: local variables referenced from a guard must be final or effectively final case Integer i when (s++) > 0 -> {} ^ Likely, the first sample should be rejected as well. With best regards, Tagir Valeev. From forax at univ-mlv.fr Tue Oct 11 16:14:39 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 11 Oct 2022 18:14:39 +0200 (CEST) Subject: [guards] Inconsistency when compiling guard with mutable local In-Reply-To: References: Message-ID: <285068231.23805986.1665504879928.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Tagir Valeev" > To: "amber-dev" > Sent: Tuesday, October 11, 2022 4:01:30 PM > Subject: [guards] Inconsistency when compiling guard with mutable local > Hello! > > Consider the following code > > class Test { > void test(Object o) { > int s = 0; > switch (new Object()) { > case Integer i when (s = 1) > 0 -> {} > default -> {} > } > } > } > > With --enable-preview it's compiled successfully (tried 20-ea+18-1281) > > If we change (s = 1) to (s++), it cannot be compiled anymore with > quite expected error message: > > Test.java:5: error: local variables referenced from a guard must be > final or effectively final > case Integer i when (s++) > 0 -> {} > ^ > > Likely, the first sample should be rejected as well. I agree. > > With best regards, > Tagir Valeev. R?mi From reinier at zwitserloot.com Thu Oct 13 14:20:52 2022 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Thu, 13 Oct 2022 07:20:52 -0700 Subject: JEP400 vs new Scanner(System.in) Message-ID: PREAMBLE: I?m not entirely certain amber-dev is the appropriate venue. If not, where should this be discussed? It?s not quite a bug but nearly so, and not quite a simple feature request either. JDK18 brought JEP400 which changes the default charset encoding to UTF-8. This, probably out of necessity, goes quite far, in that Charset.defaultCharset() is now more or less a constant - always returns UTF_8. It?s now quite difficult to retrieve the OS-configured encoding (the ?native? encoding). However, that does mean one of the most common lines in all of java?s history, is now necessarily buggy: new Scanner(System.in) is now broken. Always, unless your docs specifically state that you must feed the app UTF_8 data. Linting tools ought to flag it down as incorrect. It?s incorrect In a nasty way too: Initially it seems to work fine, but if you?re on an OS whose native encoding isn?t UTF-8, this is subtly broken; enter non-ASCII characters on the command line and the app doesn?t handle them appropriately. A bug that is literally utterly undiscoverable on macs and most linux computers, even. How can you figure out your code is broken if all the machines you test it on use UTF-8 as an OS default? This affects beginning java programmers particularly (who tend to be writing some command line-interactive apps at first). In light of Brian Goetz?s post ?Paving the Onramp? ( https://openjdk.org/projects/amber/design-notes/on-ramp) - the experience for new users is evidently of some importance to the OpenJDK team. In light of that, the current state of writing command line interactive java apps is inconsistent with that goal. The right way to read system input in a way that works in both pre- and post-JEP400 JVM editions appears to be, as far as I can tell: Charset nativeCharset = Charset.forName(System.getProperty("native.encoding", Charset.defaultEncoding().name()); Scanner sc = new Scanner(System.in, nativeCharset); I?ll risk the hyperbole: That?s.. atrocious. Hopefully I?m missing something! Breaking _thousands_ of blogs, tutorials, stack overflow answers, and books in the process, everything that contains new Scanner(System.in). Even sysin interaction that doesn?t use scanner is likely broken; the general strategy then becomes: new InputStreamReader(System.in); which suffers from the same problem. I see a few directions for trying to address this; I?m not quite sure which way would be most appropriate: - Completely re-work keyboard input, in light of *Paving the on-ramp*. Scanner has always been a problematic API if used for keyboard input, in that the default delimiter isn?t convenient. I think the single most common beginner java stackoverflow question is the bizarre interaction between scanner?s nextLine() and scanner?s next(), and to make matters considerably worse, the proper fix (which is to call .useDelimiter(?\\R?) on the scanner first) is said in less than 1% of answers; the vast majority of tutorials and answers tell you to call .nextLine() after every .nextX() call. A suboptimal suggestion (it now means using space to delimit your input is broken). Scanner is now also quite inconsistent: The constructor goes for ?internet standard?, using UTF-8 as a default even if the OS does not, but the locale *does* go by platform default, which affects double parsing amongst other things: scanner.nextDouble() will require you to use commas as fractions separator if your OS is configured to use the Dutch locale, for example. It?s weird that scanner neither fully follows common platform-independent expectations (english locale, UTF-8), nor local-platform expectation (OS-configured locale and OS-configured charset). One way out is to make a new API for ?command line apps? and take into account Paving the on-ramp?s plans when designing it. - Rewrite specifically the new Scanner(InputStream) constructor as defaulting to native encoding even when everything else in java defaults to UTF-8 now, because that constructor is 99% used for System.in. Scanner has its own File-based constructor, so new Scanner(Files.newInputStream(..)) is quite rare. - Define that constructor to act as follows: the charset used is the platform default (i.e., from JDK18 and up, UTF-8), *unless* arg == System.in is true, in which case the scanner uses native encoding. This is a bit bizarre to write in the spec but does the right thing in the most circumstances and unbreaks thousands of tutorials, blogs, and answer sites, and is most convenient to code against. That?s usually the case with voodoo magic (because this surely risks being ?too magical?): It?s convenient and does the right thing almost always, at the risk of being hard to fathom and producing convoluted spec documentation. - Attach the problem that what?s really broken isn?t so much scanner, it?s System.in itself: byte based, of course, but now that all java methods default to UTF-8, almost all interactions with it (given that most System.in interaction is char-based, not byte-based) are now also broken. Create a second field or method in System that gives you a Reader instead of an InputStream, with the OS-native encoding applied to make it. This still leaves those thousands of tutorials broken, but at least the proper code is now simply new Scanner(System.charIn()) or whatnot, instead of the atrocious snippet above. - Even less impactful, make a new method in Charset to get the native encoding without having to delve into System.getProperty(). Charset.nativeEncoding() seems like a method that should exist. Unfortunately this would be of no help to create code that works pre- and post-JEP400, but in time, having code that only works post-JEP400 is fine, I assume. - Create a new concept ?represents a stream that would use platform native encoding if characters are read/written to it?, have System.in return true for this, and have filterstreams like BufferedInputStream just pass the call through, then redefine relevant APIs such as Scanner and PrintStream (e.g. anything that internalises conversion from bytes to characters) to pick charset encoding (native vs UTF8) based on that property. This is a more robust take on ?new Scanner(System.in) should do the right thing'. Possibly the in/out/err streams that Process gives you should also have this flag set. If it was up to me, I think a multitude of steps are warranted, each relatively simple. - Create Charset.nativeEncoding(). Which simply returns Charset.forName(System.getProperty(?native.encoding?). But with the advantage that its shorter, doesn?t require knowing a magic string, and will fail at compile time if compiled against versions that predate the existence of the native.encoding property, instead of NPEs at runtime. - Create System.charIn(). Which just returns an InputStreamReader wrapped around System.in, but with native encoding applied. - Put the job of how java apps do basic command line stuff on the agenda as a thing that should probably be addressed in the next 5 years or so, maybe after the steps laid out in Paving the on-ramp are more fleshed out. - In order to avoid problems, *before* the next LTS goes out, re-spec new Scanner(System.in) to default to native encoding, specifically when the passed inputstream is identical to System.in. Don?t bother with trying to introduce an abstracted ?prefers native encoding? flag system. --Reinier Zwitserloot -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Thu Oct 13 17:06:07 2022 From: ron.pressler at oracle.com (Ron Pressler) Date: Thu, 13 Oct 2022 17:06:07 +0000 Subject: JEP400 vs new Scanner(System.in) In-Reply-To: References: Message-ID: Hi. The appropriate list is core-libs-dev, where this discussion should continue. System.in is the standard input, which may or may not be the keyboard. For keyboard input, take a look at the java.io.Console class [1], in particular its charset and reader methods. [1]: https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/io/Console.html ? Ron On 13 Oct 2022, at 16:20, Reinier Zwitserloot > wrote: PREAMBLE: I?m not entirely certain amber-dev is the appropriate venue. If not, where should this be discussed? It?s not quite a bug but nearly so, and not quite a simple feature request either. JDK18 brought JEP400 which changes the default charset encoding to UTF-8. This, probably out of necessity, goes quite far, in that Charset.defaultCharset() is now more or less a constant - always returns UTF_8. It?s now quite difficult to retrieve the OS-configured encoding (the ?native? encoding). However, that does mean one of the most common lines in all of java?s history, is now necessarily buggy: new Scanner(System.in) is now broken. Always, unless your docs specifically state that you must feed the app UTF_8 data. Linting tools ought to flag it down as incorrect. It?s incorrect In a nasty way too: Initially it seems to work fine, but if you?re on an OS whose native encoding isn?t UTF-8, this is subtly broken; enter non-ASCII characters on the command line and the app doesn?t handle them appropriately. A bug that is literally utterly undiscoverable on macs and most linux computers, even. How can you figure out your code is broken if all the machines you test it on use UTF-8 as an OS default? This affects beginning java programmers particularly (who tend to be writing some command line-interactive apps at first). In light of Brian Goetz?s post ?Paving the Onramp? (https://openjdk.org/projects/amber/design-notes/on-ramp) - the experience for new users is evidently of some importance to the OpenJDK team. In light of that, the current state of writing command line interactive java apps is inconsistent with that goal. The right way to read system input in a way that works in both pre- and post-JEP400 JVM editions appears to be, as far as I can tell: Charset nativeCharset = Charset.forName(System.getProperty("native.encoding", Charset.defaultEncoding().name()); Scanner sc = new Scanner(System.in, nativeCharset); I?ll risk the hyperbole: That?s.. atrocious. Hopefully I?m missing something! Breaking _thousands_ of blogs, tutorials, stack overflow answers, and books in the process, everything that contains new Scanner(System.in). Even sysin interaction that doesn?t use scanner is likely broken; the general strategy then becomes: new InputStreamReader(System.in); which suffers from the same problem. I see a few directions for trying to address this; I?m not quite sure which way would be most appropriate: * Completely re-work keyboard input, in light of Paving the on-ramp. Scanner has always been a problematic API if used for keyboard input, in that the default delimiter isn?t convenient. I think the single most common beginner java stackoverflow question is the bizarre interaction between scanner?s nextLine() and scanner?s next(), and to make matters considerably worse, the proper fix (which is to call .useDelimiter(?\\R?) on the scanner first) is said in less than 1% of answers; the vast majority of tutorials and answers tell you to call .nextLine() after every .nextX() call. A suboptimal suggestion (it now means using space to delimit your input is broken). Scanner is now also quite inconsistent: The constructor goes for ?internet standard?, using UTF-8 as a default even if the OS does not, but the locale does go by platform default, which affects double parsing amongst other things: scanner.nextDouble() will require you to use commas as fractions separator if your OS is configured to use the Dutch locale, for example. It?s weird that scanner neither fully follows common platform-independent expectations (english locale, UTF-8), nor local-platform expectation (OS-configured locale and OS-configured charset). One way out is to make a new API for ?command line apps? and take into account Paving the on-ramp?s plans when designing it. * Rewrite specifically the new Scanner(InputStream) constructor as defaulting to native encoding even when everything else in java defaults to UTF-8 now, because that constructor is 99% used for System.in. Scanner has its own File-based constructor, so new Scanner(Files.newInputStream(..)) is quite rare. * Define that constructor to act as follows: the charset used is the platform default (i.e., from JDK18 and up, UTF-8), unless arg == System.in is true, in which case the scanner uses native encoding. This is a bit bizarre to write in the spec but does the right thing in the most circumstances and unbreaks thousands of tutorials, blogs, and answer sites, and is most convenient to code against. That?s usually the case with voodoo magic (because this surely risks being ?too magical?): It?s convenient and does the right thing almost always, at the risk of being hard to fathom and producing convoluted spec documentation. * Attach the problem that what?s really broken isn?t so much scanner, it?s System.in itself: byte based, of course, but now that all java methods default to UTF-8, almost all interactions with it (given that most System.in interaction is char-based, not byte-based) are now also broken. Create a second field or method in System that gives you a Reader instead of an InputStream, with the OS-native encoding applied to make it. This still leaves those thousands of tutorials broken, but at least the proper code is now simply new Scanner(System.charIn()) or whatnot, instead of the atrocious snippet above. * Even less impactful, make a new method in Charset to get the native encoding without having to delve into System.getProperty(). Charset.nativeEncoding() seems like a method that should exist. Unfortunately this would be of no help to create code that works pre- and post-JEP400, but in time, having code that only works post-JEP400 is fine, I assume. * Create a new concept ?represents a stream that would use platform native encoding if characters are read/written to it?, have System.in return true for this, and have filterstreams like BufferedInputStream just pass the call through, then redefine relevant APIs such as Scanner and PrintStream (e.g. anything that internalises conversion from bytes to characters) to pick charset encoding (native vs UTF8) based on that property. This is a more robust take on ?new Scanner(System.in) should do the right thing'. Possibly the in/out/err streams that Process gives you should also have this flag set. If it was up to me, I think a multitude of steps are warranted, each relatively simple. * Create Charset.nativeEncoding(). Which simply returns Charset.forName(System.getProperty(?native.encoding?). But with the advantage that its shorter, doesn?t require knowing a magic string, and will fail at compile time if compiled against versions that predate the existence of the native.encoding property, instead of NPEs at runtime. * Create System.charIn(). Which just returns an InputStreamReader wrapped around System.in, but with native encoding applied. * Put the job of how java apps do basic command line stuff on the agenda as a thing that should probably be addressed in the next 5 years or so, maybe after the steps laid out in Paving the on-ramp are more fleshed out. * In order to avoid problems, before the next LTS goes out, re-spec new Scanner(System.in) to default to native encoding, specifically when the passed inputstream is identical to System.in. Don?t bother with trying to introduce an abstracted ?prefers native encoding? flag system. --Reinier Zwitserloot -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Oct 17 02:54:14 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 16 Oct 2022 22:54:14 -0400 Subject: Allowing cases of an enum to stand alongside cases of another type in a switch expression? Message-ID: Hello Amber Dev Team, Currently in Java, the following is permitted. sealed interface SealedInterface {} record TypeA() implements SealedInterface {} record TypeB() implements SealedInterface {} private void mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() { final SealedInterface someValue = new TypeA(); final int result = switch (someValue) { case TypeA a -> 1; case TypeB b -> 2; }; } However, the following is not permitted. sealed interface SealedInterface {} record TypeA() implements SealedInterface {} enum TypeB implements SealedInterface { B; } private void mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() { final SealedInterface someValue = new TypeA(); final int result = switch (someValue) { case TypeA a -> 1; case TypeB.B -> 2; }; } The following is also not permitted. sealed interface SealedInterface {} enum TypeA implements SealedInterface { A; } enum TypeB implements SealedInterface { B; } private void mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() { final SealedInterface someValue = TypeA.A; final int result = switch (someValue) { case TypeA.A -> 1; case TypeB.B -> 2; }; } In both of the above failing cases, I receive the following message. ScratchPad.java:132: error: constant expression required case TypeA.A -> 1; ^ It seems the only way to get rid of the error message is to split the switch expression into 2 each time we see an enum and want to know its value. Here is for the first failing example. sealed interface SealedInterface {} record TypeA() implements SealedInterface {} enum TypeB implements SealedInterface { B; } private void mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() { final SealedInterface someValue = TypeB.B; final int result = switch (someValue) { case TypeA typeA -> 1; case TypeB typeB -> switch (typeB) { case B -> 2; }; }; } And here is for the second failing example. sealed interface SealedInterface {} enum TypeA implements SealedInterface { A; } enum TypeB implements SealedInterface { B; } private void mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() { final SealedInterface someValue = TypeA.A; final int result = switch (someValue) { case TypeA typeA -> switch (typeA) { case A -> 1; }; case TypeB typeB -> switch (typeB) { case B -> 2; }; }; } I can understand the error message well enough -- an enum value is not considered a constant expression. But the following code is not only permitted, but also exhaustive, like my very first example. enum TypeABC { A, B, C, ; } private void mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() { final TypeABC someValue = TypeABC.A; final int result = switch (someValue) { case A -> 1; case B -> 2; case C -> 3; }; } I understand how switch expressions are the vehicle we are using to deliver exhaustive pattern matching for objects. However, the fact that 2 very different concepts are sharing the same vehicle (and have to take turns being the driver) means that we end up in some frustrating potholes. For starters, records and enums organically play well together. There are some concepts best modeled by an enum, and others where a record is a better choice. So if your domain of possible options encourages you to use the 2 together, why does the language make it so hard to do so when doing exhaustiveness checks? In some of the articles/videos/etc. about pattern matching by some of the language designers (Brian Goetz's Data Oriented Programming article, for example), we see a new pattern that involves using a record with no fields as form of data representation. Consider this example. sealed interface Division {} record DivideByZero() implements Division {} record DivisionResult(double answer) implements Division {} I understand and accept how, in this situation, a separate record type under the same sealed interface is a perfectly acceptable solution to model an irrepresentable state. However, because of the fact that switch expressions don't let me mix enum value cases easily with record type cases, I find myself leaning more towards record type cases as a complete replacement for enum values. This is because I don't want to deal with the head ache of trying to get exhaustiveness checking while trying to keep my problem visible on one screen. And yet, the language as is seems to outright discourage enums for switch expressions. After all, records can model everything enums can, but enums can't model everything records can. And since switch expressions make it easier to use records instead of enums, why use enums at all? But that's a serious problem. Yes, a record can model data the same way an enum can, but that doesn't mean that it is always a drop in replacement, let alone a good one. There are instances where using a class/record type as opposed to an enum value would be problematic at best for your application. I will stop considering hypotheticals and simply ask -- am I missing something? Is this something that we plan to solve later, but isn't a priority now? Is this not even a problem at all and I am looking at things the wrong way? Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Oct 17 03:08:09 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 16 Oct 2022 23:08:09 -0400 Subject: Allowing cases of an enum to stand alongside cases of another type in a switch expression? In-Reply-To: References: Message-ID: And I guess while I'm here, I will also ask -- putting aside records for a moment, was there ever an explicit reason why this was not permitted? I can understand records and enums - one is types, the other is values, so it may be hard to mix them. But this is ALL values. Is it because it isn't worth the effort? sealed interface SealedInterface {} enum TypeA implements SealedInterface { A; } enum TypeB implements SealedInterface { B; } private void mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() { final SealedInterface someValue = TypeA.A; final int result = switch (someValue) { case TypeA.A -> 1; case TypeB.B -> 2; }; } And separately, apologies in advance if these are all old discussion topics that have been brought up multiple times before. I try to look up the discussions for these on google, but I don't know if they just aren't getting indexed or something. Does this mailing list have some sort of index or search bar that allows me to look up things by topic? The best I can find is going month by month and guesstimating when the discussion would have happened at. But I'm sure there must be a better way. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Oct 17 19:55:11 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 17 Oct 2022 15:55:11 -0400 Subject: Allowing cases of an enum to stand alongside cases of another type in a switch expression? In-Reply-To: References: Message-ID: <5d72bd66-308b-8d10-3f9b-6f13f6dc722e@oracle.com> Short answer: we understand the problem, and it will eventually be addressed. Switch started with a lot of baggage, and accreted more over the years.? Fully rehabilitating switch will take some time. Support for enums in switches remains, currently, in the domain of "legacy behavior"; to complete the rehabilitation we'll need to interpret constant case labels as constant _patterns_.? This is on the list, and we're working through it, but we haven't gotten there yet. Fans of algebraic data types in Haskell will feel this gap, in Haskell's version of enums and records are the same thing: ??? data List t = Nil | Cons t (List t) Nil is like an enum; Cons is like a record.? It is perfectly reasonable to switch over something that has both, we're just not there yet. On 10/16/2022 10:54 PM, David Alayachew wrote: > Hello Amber Dev Team, > > Currently in Java, the following is permitted. > > ? ?sealed interface SealedInterface {} > ? ?record TypeA() implements SealedInterface {} > ? ?record TypeB() implements SealedInterface {} > > ? ?private void > mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() > ? ?{ > > ? ? ? final SealedInterface someValue = new TypeA(); > > ? ? ? final int result = > ? ? ? ? ?switch (someValue) > ? ? ? ? ?{ > > ? ? ? ? ? ? case TypeA a -> 1; > ? ? ? ? ? ? case TypeB b -> 2; > > ? ? ? ? ?}; > > ? ?} > > However, the following is not permitted. > > > ? ?sealed interface SealedInterface {} > ? ?record TypeA() implements SealedInterface {} > ? ?enum TypeB implements SealedInterface { B; } > > ? ?private void > mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() > ? ?{ > > ? ? ? final SealedInterface someValue = new TypeA(); > > ? ? ? final int result = > ? ? ? ? ?switch (someValue) > ? ? ? ? ?{ > > ? ? ? ? ? ? case TypeA a -> 1; > ? ? ? ? ? ? case TypeB.B -> 2; > > ? ? ? ? ?}; > > ? ?} > > The following is also not permitted. > > > ? ?sealed interface SealedInterface {} > ? ?enum TypeA implements SealedInterface { A; } > ? ?enum TypeB implements SealedInterface { B; } > > ? ?private void > mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() > ? ?{ > > ? ? ? final SealedInterface someValue = TypeA.A; > > ? ? ? final int result = > ? ? ? ? ?switch (someValue) > ? ? ? ? ?{ > > ? ? ? ? ? ? case TypeA.A -> 1; > ? ? ? ? ? ? case TypeB.B -> 2; > > ? ? ? ? ?}; > > ? ?} > > In both of the above failing cases, I receive the following message. > > ? ?ScratchPad.java:132: error: constant expression required > ? ? ? ? ? ? case TypeA.A -> 1; > ? ? ? ? ? ? ? ? ? ? ? ^ > > It seems the only way to get rid of the error message is to split the > switch expression into 2 each time we see an enum and want to know its > value. > > Here is for the first failing example. > > > ? ?sealed interface SealedInterface {} > ? ?record TypeA() implements SealedInterface {} > ? ?enum TypeB implements SealedInterface { B; } > > ? ?private void > mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() > ? ?{ > > ? ? ? final SealedInterface someValue = TypeB.B; > > ? ? ? final int result = > ? ? ? ? ?switch (someValue) > ? ? ? ? ?{ > > ? ? ? ? ? ? case TypeA typeA -> 1; > ? ? ? ? ? ? case TypeB typeB -> > ? ? ? ? ? ? ? ?switch (typeB) > ? ? ? ? ? ? ? ?{ > ? ? ? ? ? ? ? ? ? case B -> 2; > ? ? ? ? ? ? ? ?}; > > ? ? ? ? ?}; > > ? ?} > > And here is for the second failing example. > > ? ?sealed interface SealedInterface {} > ? ?enum TypeA implements SealedInterface { A; } > ? ?enum TypeB implements SealedInterface { B; } > > ? ?private void > mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() > ? ?{ > > ? ? ? final SealedInterface someValue = TypeA.A; > > ? ? ? final int result = > ? ? ? ? ?switch (someValue) > ? ? ? ? ?{ > > ? ? ? ? ? ? case TypeA typeA -> > ? ? ? ? ? ? ? ?switch (typeA) > ? ? ? ? ? ? ? ?{ > ? ? ? ? ? ? ? ? ? case A -> 1; > ? ? ? ? ? ? ? ?}; > ? ? ? ? ? ? case TypeB typeB -> > ? ? ? ? ? ? ? ?switch (typeB) > ? ? ? ? ? ? ? ?{ > ? ? ? ? ? ? ? ? ? case B -> 2; > ? ? ? ? ? ? ? ?}; > > ? ? ? ? ?}; > > ? ?} > > > I can understand the error message well enough -- an enum value is not > considered a constant expression. > > But the following code is not only permitted, but also exhaustive, > like my very first example. > > > ? ?enum TypeABC { A, B, C, ; } > > ? ?private void > mixingEnumsUnderTheSameSealedInterfaceDuringAPatternMatch() > ? ?{ > > ? ? ? final TypeABC someValue = TypeABC.A; > > ? ? ? final int result = > ? ? ? ? ?switch (someValue) > ? ? ? ? ?{ > > ? ? ? ? ? ? case A -> 1; > ? ? ? ? ? ? case B -> 2; > ? ? ? ? ? ? case C -> 3; > > ? ? ? ? ?}; > > ? ?} > > I understand how switch expressions are the vehicle we are using to > deliver exhaustive pattern matching for objects. However, the fact > that 2 very different concepts are sharing the same vehicle (and have > to take turns being the driver) means that we end up in some > frustrating potholes. > > For starters, records and enums organically play well together. There > are some concepts best modeled by an enum, and others where a record > is a better choice. So if your domain of possible options encourages > you to use the 2 together, why does the language make it so hard to do > so when doing exhaustiveness checks? > > In some of the articles/videos/etc. about pattern matching by some of > the language designers (Brian Goetz's Data Oriented Programming > article, for example), we see a new pattern that involves using a > record with no fields as form of data representation. > > Consider this example. > > ? ?sealed interface Division {} > ? ?record DivideByZero() implements Division {} > ? ?record DivisionResult(double answer) implements Division {} > > I understand and accept how, in this situation, a separate record type > under the same sealed interface is a perfectly acceptable solution to > model an irrepresentable state. > > However, because of the fact that switch expressions don't let me mix > enum value cases easily with record type cases, I find myself leaning > more towards record type cases as a complete replacement for enum > values. This is because I don't want to deal with the head ache of > trying to get exhaustiveness checking while trying to keep my problem > visible on one screen. > > And yet, the language as is seems to outright discourage enums for > switch expressions. After all, records can model everything enums can, > but enums can't model everything records can. And since switch > expressions make it easier to use records instead of enums, why use > enums at all? > > But that's a serious problem. Yes, a record can model data the same > way an enum can, but that doesn't mean that it is always a drop in > replacement, let alone a good one. There are instances where using a > class/record type as opposed to an enum value would be problematic at > best for your application. > > I will stop considering hypotheticals and simply ask -- am I missing > something? Is this something that we plan to solve later, but isn't a > priority now? Is this not even a problem at all and I am looking at > things the wrong way? > > Thank you for your time and help! > David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Oct 17 21:00:10 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 17 Oct 2022 17:00:10 -0400 Subject: Allowing cases of an enum to stand alongside cases of another type in a switch expression? In-Reply-To: <5d72bd66-308b-8d10-3f9b-6f13f6dc722e@oracle.com> References: <5d72bd66-308b-8d10-3f9b-6f13f6dc722e@oracle.com> Message-ID: Hello Brian, Thank you for your response! That clarifies everything for me now. I'm very happy to hear that enums are on their way into the new world of pattern-matching for Java. > data List t = Nil | Cons t (List t) This example is very helpful. I see exactly what you mean about NIL being an enum-like object. And I also see what you mean progress wise too. We are not quite there yet with NIL, but I am tempted to say that Cons has functional parity with Java Records. That really goes to show just how far Java has traveled in so short a time. It's also hilarious because it was while studying Haskell and Scala's typeclasses/pattern matching that I noticed this discrepancy and made a post. Another commenter on a previous thread [1] suggested I look into Scala's type classes to better understand how they can solve the problem I had. They had originally suggested Scala, but when studying the concept further, my research brought me to Haskell too. I am still trying to wrap my brain around how those 2 concepts work, but I can definitely see that there is a lot of value and utility in both. Thank you for your help and reassurance! David Alayachew [1] https://mail.openjdk.org/pipermail/amber-dev/2022-September/007459.html -------------- next part -------------- An HTML attachment was scrubbed... URL: From kamil at sevecek.net Tue Oct 18 08:35:22 2022 From: kamil at sevecek.net (=?UTF-8?B?S2FtaWwgxaBldmXEjWVr?=) Date: Tue, 18 Oct 2022 10:35:22 +0200 Subject: JEP400 vs new Scanner(System.in) In-Reply-To: References: Message-ID: Hi! Perhaps core-libs-dev is the more appropriate mailing list, but there are hundreds of posts a month there. I'm not sure whether the severity of the problem would require a more fundamental solution. So I will reply here as well, because I see that Brian Goetz reviewed and endorsed the JEP-400 and I would like to see him endorsing a rescue action as well to keep my trust in the Java platform. If there is a higher priority than CRITICAL, then this is the case. If left as it is, this JEP will completely ruin Java as a starting language because no simple beginner's example from the web will work anymore and any new will look extremely complicated. Reinier described it very realistically. Just a side note for the java.io.Console: This class does not work in IDEs. System.console() returns null in IDEs. It only works when Java is invoked from the native OS console. Therefore it is very much useless. ? Kamil Sevecek On Thu, 13 Oct 2022 at 19:07, Ron Pressler wrote: > Hi. > > The appropriate list is core-libs-dev, where this discussion should > continue. > > System.in is the standard input, which may or may not be the keyboard. For > keyboard input, take a look at the java.io.Console class [1], in particular > its charset and reader methods. > > [1]: > https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/io/Console.html > > ? Ron > > On 13 Oct 2022, at 16:20, Reinier Zwitserloot > wrote: > > PREAMBLE: I?m not entirely certain amber-dev is the appropriate venue. If > not, where should this be discussed? It?s not quite a bug but nearly so, > and not quite a simple feature request either. > > JDK18 brought JEP400 which changes the default charset encoding to UTF-8. > This, probably out of necessity, goes quite far, in that > Charset.defaultCharset() is now more or less a constant - always returns > UTF_8. It?s now quite difficult to retrieve the OS-configured encoding (the > ?native? encoding). > > However, that does mean one of the most common lines in all of java?s > history, is now necessarily buggy: new Scanner(System.in) is now broken. > Always, unless your docs specifically state that you must feed the app > UTF_8 data. Linting tools ought to flag it down as incorrect. It?s > incorrect In a nasty way too: Initially it seems to work fine, but if > you?re on an OS whose native encoding isn?t UTF-8, this is subtly broken; > enter non-ASCII characters on the command line and the app doesn?t handle > them appropriately. A bug that is literally utterly undiscoverable on macs > and most linux computers, even. How can you figure out your code is broken > if all the machines you test it on use UTF-8 as an OS default? > > This affects beginning java programmers particularly (who tend to be > writing some command line-interactive apps at first). In light of Brian > Goetz?s post ?Paving the Onramp? ( > https://openjdk.org/projects/amber/design-notes/on-ramp) - the experience > for new users is evidently of some importance to the OpenJDK team. In light > of that, the current state of writing command line interactive java apps is > inconsistent with that goal. > > The right way to read system input in a way that works in both pre- and > post-JEP400 JVM editions appears to be, as far as I can tell: > > Charset nativeCharset = Charset.forName(System.getProperty("native.encoding", Charset.defaultEncoding().name()); > Scanner sc = new Scanner(System.in, nativeCharset); > > > I?ll risk the hyperbole: That?s.. atrocious. Hopefully I?m missing > something! > > Breaking _thousands_ of blogs, tutorials, stack overflow answers, and > books in the process, everything that contains new Scanner(System.in). > Even sysin interaction that doesn?t use scanner is likely broken; the > general strategy then becomes: > > new InputStreamReader(System.in); > > > which suffers from the same problem. > > I see a few directions for trying to address this; I?m not quite sure > which way would be most appropriate: > > > - Completely re-work keyboard input, in light of *Paving the on-ramp*. > Scanner has always been a problematic API if used for keyboard input, in > that the default delimiter isn?t convenient. I think the single most common > beginner java stackoverflow question is the bizarre interaction between > scanner?s nextLine() and scanner?s next(), and to make matters > considerably worse, the proper fix (which is to call > .useDelimiter(?\\R?) on the scanner first) is said in less than 1% of > answers; the vast majority of tutorials and answers tell you to call > .nextLine() after every .nextX() call. A suboptimal suggestion (it now > means using space to delimit your input is broken). Scanner is now also > quite inconsistent: The constructor goes for ?internet standard?, using > UTF-8 as a default even if the OS does not, but the locale *does* go > by platform default, which affects double parsing amongst other things: > scanner.nextDouble() will require you to use commas as fractions > separator if your OS is configured to use the Dutch locale, for example. > It?s weird that scanner neither fully follows common platform-independent > expectations (english locale, UTF-8), nor local-platform expectation > (OS-configured locale and OS-configured charset). One way out is to make a > new API for ?command line apps? and take into account Paving the on-ramp?s > plans when designing it. > - Rewrite specifically the new Scanner(InputStream) constructor as > defaulting to native encoding even when everything else in java defaults to > UTF-8 now, because that constructor is 99% used for System.in. Scanner > has its own File-based constructor, so new > Scanner(Files.newInputStream(..)) is quite rare. > - Define that constructor to act as follows: the charset used is the > platform default (i.e., from JDK18 and up, UTF-8), *unless* arg == > System.in is true, in which case the scanner uses native encoding. > This is a bit bizarre to write in the spec but does the right thing in the > most circumstances and unbreaks thousands of tutorials, blogs, and answer > sites, and is most convenient to code against. That?s usually the case with > voodoo magic (because this surely risks being ?too magical?): It?s > convenient and does the right thing almost always, at the risk of being > hard to fathom and producing convoluted spec documentation. > - Attach the problem that what?s really broken isn?t so much scanner, > it?s System.in itself: byte based, of course, but now that all java > methods default to UTF-8, almost all interactions with it (given that most > System.in interaction is char-based, not byte-based) are now also > broken. Create a second field or method in System that gives you a > Reader instead of an InputStream, with the OS-native encoding applied > to make it. This still leaves those thousands of tutorials broken, but at > least the proper code is now simply new Scanner(System.charIn()) or > whatnot, instead of the atrocious snippet above. > - Even less impactful, make a new method in Charset to get the native > encoding without having to delve into System.getProperty(). > Charset.nativeEncoding() seems like a method that should exist. > Unfortunately this would be of no help to create code that works pre- and > post-JEP400, but in time, having code that only works post-JEP400 is fine, > I assume. > - Create a new concept ?represents a stream that would use platform > native encoding if characters are read/written to it?, have System.in > return true for this, and have filterstreams like BufferedInputStream just > pass the call through, then redefine relevant APIs such as Scanner and > PrintStream (e.g. anything that internalises conversion from bytes to > characters) to pick charset encoding (native vs UTF8) based on that > property. This is a more robust take on ?new Scanner(System.in) should > do the right thing'. Possibly the in/out/err streams that Process gives > you should also have this flag set. > > > > If it was up to me, I think a multitude of steps are warranted, each > relatively simple. > > > - Create Charset.nativeEncoding(). Which simply returns > Charset.forName(System.getProperty(?native.encoding?). But with the > advantage that its shorter, doesn?t require knowing a magic string, and > will fail at compile time if compiled against versions that predate the > existence of the native.encoding property, instead of NPEs at runtime. > - Create System.charIn(). Which just returns an InputStreamReader > wrapped around System.in, but with native encoding applied. > - Put the job of how java apps do basic command line stuff on the > agenda as a thing that should probably be addressed in the next 5 years or > so, maybe after the steps laid out in Paving the on-ramp are more fleshed > out. > - In order to avoid problems, *before* the next LTS goes out, re-spec new > Scanner(System.in) to default to native encoding, specifically when > the passed inputstream is identical to System.in. Don?t bother with > trying to introduce an abstracted ?prefers native encoding? flag system. > > > --Reinier Zwitserloot > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mark.reinhold at oracle.com Tue Oct 18 16:56:41 2022 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Tue, 18 Oct 2022 16:56:41 +0000 Subject: New candidate JEP: 432: Record Patterns (Second Preview) Message-ID: <20221018165640.C28905488A9@eggemoggin.niobe.net> https://openjdk.org/jeps/432 Summary: Enhance the Java programming language with to deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing. This is a . - Mark From mark.reinhold at oracle.com Tue Oct 18 16:56:42 2022 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Tue, 18 Oct 2022 16:56:42 +0000 Subject: New candidate JEP: 433: Pattern Matching for switch (Fourth Preview) Message-ID: <20221018165642.45F5C5488AB@eggemoggin.niobe.net> https://openjdk.org/jeps/433 Summary: Enhance the Java programming language with pattern matching for expressions and statements. Extending pattern matching to allows an expression to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed concisely and safely. This is a . - Mark From mark.reinhold at oracle.com Tue Oct 18 17:04:09 2022 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Tue, 18 Oct 2022 17:04:09 +0000 Subject: New candidate JEP: 432: Record Patterns (Second Preview) Message-ID: <20221018170409.42A125488B4@eggemoggin.niobe.net> // Corrected https://openjdk.org/jeps/432 Summary: Enhance the Java programming language with record patterns to deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing. This is a preview language feature. - Mark From mark.reinhold at oracle.com Tue Oct 18 17:04:10 2022 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Tue, 18 Oct 2022 17:04:10 +0000 Subject: New candidate JEP: 433: Pattern Matching for switch (Fourth Preview) Message-ID: <20221018170409.9134B5488B6@eggemoggin.niobe.net> // Corrected https://openjdk.org/jeps/433 Summary: Enhance the Java programming language with pattern matching for switch expressions and statements. Extending pattern matching to switch allows an expression to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed concisely and safely. This is a preview language feature. - Mark From archie.cobbs at gmail.com Mon Oct 31 19:18:19 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 31 Oct 2022 14:18:19 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> Message-ID: This is an old thread which I've been looking at again lately. To summarize, the idea is to relax the JLS rules about code prior to super()/this() invocation to more closely match what the JVM allows, which is field assignments and any code that doesn't reference 'this'. Relevant issues are JDK-8194743 and JDK-8193760 . In order to make this idea less theoretical, I've prototyped all the required compiler changes. There is also a write-up describing them . There are a few interesting subtleties, e.g. relating to initialization blocks, and there is also a sub-question, which is whether to allow invoking super()/this() within a try block (this would have to be under the restriction that any catch or finally clause must not return normally). Currently that's not possible without a small JVM change, which I also think might be worth considering to really round out this new feature. See the writeup for details. To see some examples of what would be allowed, see this unit test . The compiler changes are here . Thoughts? -Archie On Thu, Jan 17, 2019 at 8:42 AM Brian Goetz wrote: > Some things have improved for this feature since we last talked; several > verifier issues that this would have pushed on have been resolved. So it?s > moved from the ?way too expensive for the benefit? category into the ?there > are lots of things we can do, is this really what we want to spend our > effort and complexity budget on? category. > > My view on this is that while there?s nothing wrong with it, it?s also a > pretty minor wart. If this fell out of a bigger feature, I?d certainly not > object, but I?d rather spend the effort and complexity budget on things > that have broader benefit. > > > On Jan 16, 2019, at 5:48 PM, Archie Cobbs > wrote: > > > > I'm curious what are people's current thoughts on loosening the > > requirements for super() invocation in the context of Amber, e.g.: > > > > public class MyInputStream extends FilterInputStream { > > public MyInputStream(InputStream in) { > > if (in == null) > > throw new IllegalArgumentException("null input"); > > super(in); // look ma! > > } > > } > > > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Oct 31 22:07:59 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 31 Oct 2022 18:07:59 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> Message-ID: <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> This looks like some nice progress. I would really like it if we had some warnings to detect more of the cases where `this` might escape construction, such as passing `this` explicitly to a method, invoking a non-final instance method, or invoking an instance method whose implementation is in a superclass.? (Same for use of `this` in init blocks.) I think the "try" part is likely to be off the table; too intrusive for too little value. Have you looked at the spec to see what would need to be adjusted? On 10/31/2022 3:18 PM, Archie Cobbs wrote: > This is an old thread which I've been looking at again lately. > > To summarize, the idea is to relax the JLS rules about code prior to > super()/this() invocation to more closely match what the JVM allows, > which is field assignments and any code that doesn't reference 'this'. > Relevant issues are JDK-8194743 > and JDK-8193760 > . > > In order to make this idea less theoretical, I've prototyped all the > required compiler changes. There is also a write-up describing them > . > > There are a few interesting subtleties, e.g. relating to > initialization blocks, and there is also a sub-question, which is > whether to allow invoking super()/this() within a try block (this > would have to be under the restriction that any catch or finally > clause must not return normally). Currently that's not possible > without a small JVM change, which I also think might be worth > considering to really round out this new feature. See the writeup for > details. > > To see some examples of what would be allowed, see this unit test > . > The compiler changes are here > . > > Thoughts? > > -Archie > > On Thu, Jan 17, 2019 at 8:42 AM Brian Goetz > wrote: > > Some things have improved for this feature since we last talked; > several verifier issues that this would have pushed on have been > resolved. So it?s moved from the ?way too expensive for the > benefit? category into the ?there are lots of things we can do, is > this really what we want to spend our effort and complexity budget > on? category. > > My view on this is that while there?s nothing wrong with it, it?s > also a pretty minor wart.? If this fell out of a bigger feature, > I?d certainly not object, but I?d rather spend the effort and > complexity budget on things that have broader benefit. > > > On Jan 16, 2019, at 5:48 PM, Archie Cobbs > wrote: > > > > I'm curious what are people's current thoughts on loosening the > > requirements for super() invocation in the context of Amber, e.g.: > > > >? ? public class MyInputStream extends FilterInputStream { > >? ? ? ? public MyInputStream(InputStream in) { > >? ? ? ? ? ? if (in == null) > >? ? ? ? ? ? ? ? throw new IllegalArgumentException("null input"); > >? ? ? ? ? ? super(in);? ? ? // look ma! > >? ? ? ? } > >? ? } > > > > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From kevinb at google.com Mon Oct 31 23:36:40 2022 From: kevinb at google.com (Kevin Bourrillion) Date: Mon, 31 Oct 2022 16:36:40 -0700 Subject: Loosening requirements for super() invocation In-Reply-To: <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> Message-ID: On Mon, Oct 31, 2022 at 3:08 PM Brian Goetz wrote: > > I would really like it if we had some warnings to detect more of the cases > where `this` might escape construction, such as passing `this` explicitly > to a method, invoking a non-final instance method, or invoking an instance > method whose implementation is in a superclass. (Same for use of `this` in > init blocks.) > Just to say: that would be awesome. Escaping thisses are really confounding to static analysis, because few method bodies can ever be known to be running safely past the construct-initialize gap. On the main topic, for whatever it's worth, if more things like this became possible I'm not 100% sure we'd allow them in our codebase anyway. The best practice is still "don't do work in constructors": have a method get all the field values ready so the constructor has only to store them. The reason again is that the construct-init gap is a nasty place for code to live in, where bugs are hard to reliably prevent. Fortunately methods wrapping constructors often serve better as external API for several other reasons anyway. That doesn't constitute an argument *against* the feature. > On 10/31/2022 3:18 PM, Archie Cobbs wrote: > > This is an old thread which I've been looking at again lately. > > To summarize, the idea is to relax the JLS rules about code prior to > super()/this() invocation to more closely match what the JVM allows, which > is field assignments and any code that doesn't reference 'this'. Relevant > issues are JDK-8194743 and > JDK-8193760 . > > In order to make this idea less theoretical, I've prototyped all the > required compiler changes. There is also a write-up describing them > > . > > There are a few interesting subtleties, e.g. relating to initialization > blocks, and there is also a sub-question, which is whether to allow > invoking super()/this() within a try block (this would have to be under the > restriction that any catch or finally clause must not return normally). > Currently that's not possible without a small JVM change, which I also > think might be worth considering to really round out this new feature. See > the writeup for details. > > To see some examples of what would be allowed, see this unit test > . > The compiler changes are here > > . > > Thoughts? > > -Archie > > On Thu, Jan 17, 2019 at 8:42 AM Brian Goetz > wrote: > >> Some things have improved for this feature since we last talked; several >> verifier issues that this would have pushed on have been resolved. So it?s >> moved from the ?way too expensive for the benefit? category into the ?there >> are lots of things we can do, is this really what we want to spend our >> effort and complexity budget on? category. >> >> My view on this is that while there?s nothing wrong with it, it?s also a >> pretty minor wart. If this fell out of a bigger feature, I?d certainly not >> object, but I?d rather spend the effort and complexity budget on things >> that have broader benefit. >> >> > On Jan 16, 2019, at 5:48 PM, Archie Cobbs >> wrote: >> > >> > I'm curious what are people's current thoughts on loosening the >> > requirements for super() invocation in the context of Amber, e.g.: >> > >> > public class MyInputStream extends FilterInputStream { >> > public MyInputStream(InputStream in) { >> > if (in == null) >> > throw new IllegalArgumentException("null input"); >> > super(in); // look ma! >> > } >> > } >> > >> > > -- > Archie L. Cobbs > > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com -------------- next part -------------- An HTML attachment was scrubbed... URL: