From amaembo at gmail.com Thu Aug 3 10:07:24 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 3 Aug 2023 12:07:24 +0200 Subject: [string-templates] Covariant signature of 'process' method Message-ID: Hello! It's possible to create a custom string template with covariant process() method signature. For example, we can relax the throws statement: public class Test { static class MyProcessor implements StringTemplate.Processor { @Override public Integer process(StringTemplate stringTemplate) { return 123; } } public static void main(String[] args) { MyProcessor proc = new MyProcessor<>(); Integer i = proc."hello"; } } Now, it's not compilable: java: unreported exception java.lang.Exception; must be caught or declared to be thrown However, it's evident and can be statically checked that the exception is not possible. Covariant return type is also possible: public class Test { static class MyProcessor implements StringTemplate.Processor { @Override public Integer process(StringTemplate stringTemplate) { return 123; } } public static void main(String[] args) { MyProcessor proc = new MyProcessor(); Integer i = proc."hello"; } } Now, it's not compilable: java: incompatible types: java.lang.Object cannot be converted to java.lang.Integer This requires passing exact types through the inheritance hierarchy which might be inflexible and annoying. I may probably want to declare `abstract class AbstractProcessor implements Processor` and have any covariant signatures in subtypes without declaring new type parameters and forward them through the hierarchy. Java has another implicit way to call methods: try-with-resources and AutoCloseable interface. There we have no exception type parameter, but covariant signatures in subclass close() methods are respected at the try-with-resource use sites. Probably it would be more consistent and convenient to respect covariant signatures in string templates as well? With best regards, Tagir Valeev. From amaembo at gmail.com Thu Aug 3 10:10:13 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 3 Aug 2023 12:10:13 +0200 Subject: [Correction] Re: [string-templates] Covariant signature of 'process' method In-Reply-To: References: Message-ID: Unfortunately, I mixed the sample code. The first code sample in my previous letter should read as: public class Test { static class MyProcessor implements StringTemplate.Processor { @Override public Integer process(StringTemplate stringTemplate) { return 123; } } public static void main(String[] args) { MyProcessor proc = new MyProcessor(); Integer i = proc."hello"; } } The second sample should read as: public class Test { static class MyProcessor implements StringTemplate.Processor { @Override public Integer process(StringTemplate stringTemplate) { return 123; } } public static void main(String[] args) { MyProcessor proc = new MyProcessor(); Integer i = proc."hello"; } } Sorry for the confusion. With best regards, Tagir Valeev. On Thu, Aug 3, 2023 at 12:07?PM Tagir Valeev wrote: > > Hello! > > It's possible to create a custom string template with covariant > process() method signature. For example, we can relax the throws > statement: > > public class Test { > static class MyProcessor implements > StringTemplate.Processor { > @Override > public Integer process(StringTemplate stringTemplate) { > return 123; > } > } > > public static void main(String[] args) { > MyProcessor proc = new MyProcessor<>(); > Integer i = proc."hello"; > } > } > > Now, it's not compilable: > java: unreported exception java.lang.Exception; must be caught or > declared to be thrown > > However, it's evident and can be statically checked that the exception > is not possible. > > Covariant return type is also possible: > > public class Test { > static class MyProcessor implements > StringTemplate.Processor { > @Override > public Integer process(StringTemplate stringTemplate) { > return 123; > } > } > > public static void main(String[] args) { > MyProcessor proc = new MyProcessor(); > Integer i = proc."hello"; > } > } > > Now, it's not compilable: > java: incompatible types: java.lang.Object cannot be converted to > java.lang.Integer > > This requires passing exact types through the inheritance hierarchy > which might be inflexible and annoying. I may probably want to declare > `abstract class AbstractProcessor implements Processor Throwable>` and have any covariant signatures in subtypes without > declaring new type parameters and forward them through the hierarchy. > > Java has another implicit way to call methods: try-with-resources and > AutoCloseable interface. There we have no exception type parameter, > but covariant signatures in subclass close() methods are respected at > the try-with-resource use sites. Probably it would be more consistent > and convenient to respect covariant signatures in string templates as > well? > > With best regards, > Tagir Valeev. From amaembo at gmail.com Thu Aug 3 11:51:55 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 3 Aug 2023 13:51:55 +0200 Subject: [Correction] Re: [string-templates] Covariant signature of 'process' method In-Reply-To: References: Message-ID: Additional thoughts on this topic. 1. Currently, it looks impossible for the processor to declare several disjoin exceptions. E.g., I cannot do this: static class CovariantException implements StringTemplate.Processor { @Override public Integer process(StringTemplate stringTemplate) throws IOException, SQLException { return 123; } } public static void main(String[] args) { CovariantException proc = new CovariantException(); try { Integer i = proc."hello"; } catch (IOException e) { // handle } catch (SQLException e) { // handle } } I must declare and handle the closest supertype exception (which is simply java.lang.Exception), which makes the exception handling much more clumsy, as in this case I will also catch unrelated RuntimeExceptions. Or I can declare a separate rethrowing catch(RuntimeException ex), but this is very unfortunate to force people to do this. 2. Annotations could be added to the process() method and handled by third-party tools like annotation processors or static analyzers. For example, Checker framework provides Pure annotation to annotate methods that don't produce side-effects. We can do this: static class Covariant implements StringTemplate.Processor { @Override @Pure public Integer process(StringTemplate stringTemplate) { return 123; } } public static void main(String[] args) { Covariant proc = new Covariant(); Object i = proc."hello"; } And we will assume that the static analyzer will respect the annotation. To do this, a static analyzer should resolve the implicit call in template expression into Covariant::process. However, Java compiler resolves it to Processor::process (as covariant return type is ignored). It sounds really strange to get the return type from the supermethod while the annotations from the most specific method. Of course, we cannot put our own annotations to Processor::process. With best regards, Tagir Valeev. On Thu, Aug 3, 2023 at 12:10?PM Tagir Valeev wrote: > > Unfortunately, I mixed the sample code. The first code sample in my > previous letter should read as: > > public class Test { > static class MyProcessor implements > StringTemplate.Processor { > @Override > public Integer process(StringTemplate stringTemplate) { > return 123; > } > } > > public static void main(String[] args) { > MyProcessor proc = new MyProcessor(); > Integer i = proc."hello"; > } > } > > The second sample should read as: > > public class Test { > static class MyProcessor implements > StringTemplate.Processor { > @Override > public Integer process(StringTemplate stringTemplate) { > return 123; > } > } > > public static void main(String[] args) { > MyProcessor proc = new MyProcessor(); > Integer i = proc."hello"; > } > } > > Sorry for the confusion. > > With best regards, > Tagir Valeev. > > On Thu, Aug 3, 2023 at 12:07?PM Tagir Valeev wrote: > > > > Hello! > > > > It's possible to create a custom string template with covariant > > process() method signature. For example, we can relax the throws > > statement: > > > > public class Test { > > static class MyProcessor implements > > StringTemplate.Processor { > > @Override > > public Integer process(StringTemplate stringTemplate) { > > return 123; > > } > > } > > > > public static void main(String[] args) { > > MyProcessor proc = new MyProcessor<>(); > > Integer i = proc."hello"; > > } > > } > > > > Now, it's not compilable: > > java: unreported exception java.lang.Exception; must be caught or > > declared to be thrown > > > > However, it's evident and can be statically checked that the exception > > is not possible. > > > > Covariant return type is also possible: > > > > public class Test { > > static class MyProcessor implements > > StringTemplate.Processor { > > @Override > > public Integer process(StringTemplate stringTemplate) { > > return 123; > > } > > } > > > > public static void main(String[] args) { > > MyProcessor proc = new MyProcessor(); > > Integer i = proc."hello"; > > } > > } > > > > Now, it's not compilable: > > java: incompatible types: java.lang.Object cannot be converted to > > java.lang.Integer > > > > This requires passing exact types through the inheritance hierarchy > > which might be inflexible and annoying. I may probably want to declare > > `abstract class AbstractProcessor implements Processor > Throwable>` and have any covariant signatures in subtypes without > > declaring new type parameters and forward them through the hierarchy. > > > > Java has another implicit way to call methods: try-with-resources and > > AutoCloseable interface. There we have no exception type parameter, > > but covariant signatures in subclass close() methods are respected at > > the try-with-resource use sites. Probably it would be more consistent > > and convenient to respect covariant signatures in string templates as > > well? > > > > With best regards, > > Tagir Valeev. From forax at univ-mlv.fr Thu Aug 3 15:35:16 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 3 Aug 2023 17:35:16 +0200 (CEST) Subject: [Correction] Re: [string-templates] Covariant signature of 'process' method In-Reply-To: References: Message-ID: <905766385.8014074.1691076916130.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "Tagir Valeev" > To: "amber-spec-experts" > Sent: Thursday, August 3, 2023 1:51:55 PM > Subject: Re: [Correction] Re: [string-templates] Covariant signature of 'process' method Hi Tagir, > Additional thoughts on this topic. > > 1. Currently, it looks impossible for the processor to declare several > disjoin exceptions. E.g., I cannot do this: > > static class CovariantException implements > StringTemplate.Processor { > @Override > public Integer process(StringTemplate stringTemplate) throws > IOException, SQLException { > return 123; > } > } > > public static void main(String[] args) { > CovariantException proc = new CovariantException(); > try { > Integer i = proc."hello"; > } catch (IOException e) { > // handle > } catch (SQLException e) { > // handle > } > } > > I must declare and handle the closest supertype exception (which is > simply java.lang.Exception), which makes the exception handling much > more clumsy, as in this case I will also catch unrelated > RuntimeExceptions. Or I can declare a separate rethrowing > catch(RuntimeException ex), but this is very unfortunate to force > people to do this. Yes, sadly this is a well known limitation of the Java generics, a type variable can not represent two disjoint exception types. BTW, this is the reason why the Stream API does not try to capture exceptions, in filter, map, etc, the Java type system is too weak for that. > > 2. Annotations could be added to the process() method and handled by > third-party tools like annotation processors or static analyzers. For > example, Checker framework provides Pure annotation to annotate > methods that don't produce side-effects. We can do this: > > static class Covariant implements StringTemplate.Processor RuntimeException> { > @Override > @Pure > public Integer process(StringTemplate stringTemplate) { > return 123; > } > } > > public static void main(String[] args) { > Covariant proc = new Covariant(); > Object i = proc."hello"; > } > > And we will assume that the static analyzer will respect the > annotation. To do this, a static analyzer should resolve the implicit > call in template expression into Covariant::process. However, Java > compiler resolves it to Processor::process (as covariant return type > is ignored). It sounds really strange to get the return type from the > supermethod while the annotations from the most specific method. Of > course, we cannot put our own annotations to Processor::process. This looks like a bug of the javac implementation, the virtual call should be Covariant::process and not Processor::process in this example. > > With best regards, > Tagir Valeev. regards, R?mi > > On Thu, Aug 3, 2023 at 12:10?PM Tagir Valeev wrote: >> >> Unfortunately, I mixed the sample code. The first code sample in my >> previous letter should read as: >> >> public class Test { >> static class MyProcessor implements >> StringTemplate.Processor { >> @Override >> public Integer process(StringTemplate stringTemplate) { >> return 123; >> } >> } >> >> public static void main(String[] args) { >> MyProcessor proc = new MyProcessor(); >> Integer i = proc."hello"; >> } >> } >> >> The second sample should read as: >> >> public class Test { >> static class MyProcessor implements >> StringTemplate.Processor { >> @Override >> public Integer process(StringTemplate stringTemplate) { >> return 123; >> } >> } >> >> public static void main(String[] args) { >> MyProcessor proc = new MyProcessor(); >> Integer i = proc."hello"; >> } >> } >> >> Sorry for the confusion. >> >> With best regards, >> Tagir Valeev. >> >> On Thu, Aug 3, 2023 at 12:07?PM Tagir Valeev wrote: >> > >> > Hello! >> > >> > It's possible to create a custom string template with covariant >> > process() method signature. For example, we can relax the throws >> > statement: >> > >> > public class Test { >> > static class MyProcessor implements >> > StringTemplate.Processor { >> > @Override >> > public Integer process(StringTemplate stringTemplate) { >> > return 123; >> > } >> > } >> > >> > public static void main(String[] args) { >> > MyProcessor proc = new MyProcessor<>(); >> > Integer i = proc."hello"; >> > } >> > } >> > >> > Now, it's not compilable: >> > java: unreported exception java.lang.Exception; must be caught or >> > declared to be thrown >> > >> > However, it's evident and can be statically checked that the exception >> > is not possible. >> > >> > Covariant return type is also possible: >> > >> > public class Test { >> > static class MyProcessor implements >> > StringTemplate.Processor { >> > @Override >> > public Integer process(StringTemplate stringTemplate) { >> > return 123; >> > } >> > } >> > >> > public static void main(String[] args) { >> > MyProcessor proc = new MyProcessor(); >> > Integer i = proc."hello"; >> > } >> > } >> > >> > Now, it's not compilable: >> > java: incompatible types: java.lang.Object cannot be converted to >> > java.lang.Integer >> > >> > This requires passing exact types through the inheritance hierarchy >> > which might be inflexible and annoying. I may probably want to declare >> > `abstract class AbstractProcessor implements Processor> > Throwable>` and have any covariant signatures in subtypes without >> > declaring new type parameters and forward them through the hierarchy. >> > >> > Java has another implicit way to call methods: try-with-resources and >> > AutoCloseable interface. There we have no exception type parameter, >> > but covariant signatures in subclass close() methods are respected at >> > the try-with-resource use sites. Probably it would be more consistent >> > and convenient to respect covariant signatures in string templates as >> > well? >> > >> > With best regards, > > > Tagir Valeev. From amaembo at gmail.com Mon Aug 14 12:08:37 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 14 Aug 2023 14:08:37 +0200 Subject: [patterns] Mixed constant-pattern cases Message-ID: Hello! I've noticed that javac accepts this code: enum X {A, B} void test(Object obj) { switch (obj) { case String _, X.B -> System.out.println("B or String"); default -> System.out.println("other"); } } public static void main(String[] args) { new Test().test("ddd"); } At the same time, it rejects if the order of label elements is reverted: void test(Object obj) { switch (obj) { case X.B, String _ -> System.out.println("B or String"); default -> System.out.println("other"); } } Now "java: expected" compilation error is displayed. So, I've reported a javac issue here: https://bugs.openjdk.org/browse/JDK-8314216 However, the expected behavior could be discussed. As per spec draft [1], patterns and constants cannot be mixed in the same switch label: it's either "case CaseConstant {, CaseConstant}", or "case CasePattern{, CasePattern } [ Guard ]". However, mixing them might be useful, and it already partially works in javac. Probably it would be good to update this in the spec? With best regards, Tagir Valeev [1] https://cr.openjdk.org/~abimpoudis/unnamed/jep443-20230322/specs/unnamed-jls.html#jls-14.11.1 From amaembo at gmail.com Mon Aug 14 12:15:42 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 14 Aug 2023 14:15:42 +0200 Subject: [patterns] Several patterns and guards Message-ID: Hello! Currently, when the switch label contains several patterns, only one guard could be declared, which is applied to all the patterns at once. In other words, the following code is not possible: void test(Object obj) { switch (obj) { case Integer _ when ((Integer) obj) > 0, String _ when !((String) obj).isEmpty() -> System.out.println("Positive number or non-empty string"); default -> System.out.println("other"); } } Does it make sense to lift this restriction? Probably it could be useful to declare separate guards? Ideally it should be possible to be able to declare a pattern variable, which is visible inside the pattern-specific guard only (but not inside the rule body). Another confusing thing here: void test(Object obj) { switch (obj) { case Integer _, String _ when !((String) obj).isEmpty() -> System.out.println("Number or non-empty string"); default -> System.out.println("other"); } } Now, the guard is applied even if obj is Integer (resulting in ClassCastException). This is not quite evident from the code. We may say that 'when' precedence is lower than ',' precedence, but people may expect the opposite. Should not we reconsider this and make guard a part of the lebel element, rather than the part of the whole label? With best regards, Tagir Valeev. From fredt at users.sourceforge.net Mon Aug 14 12:50:46 2023 From: fredt at users.sourceforge.net (Fred Toussi) Date: Mon, 14 Aug 2023 13:50:46 +0100 Subject: [patterns] Several patterns and guards In-Reply-To: References: Message-ID: Indeed 'when' should be treated as a qualifier for 'Integer _' and 'String _' rather than the whole 'case ...' and there shouldn't be a need for the casts. void test(Object obj) { switch (obj) { case Integer _ when obj > 0, String _ when obj.isEmpty() -> System.out.println("Positive number or non-empty string"); default -> System.out.println("other"); } On Mon, Aug 14, 2023, at 13:15, Tagir Valeev wrote: > Hello! > > Currently, when the switch label contains several patterns, only one > guard could be declared, which is applied to all the patterns at once. > In other words, the following code is not possible: > > void test(Object obj) { > switch (obj) { > case Integer _ when ((Integer) obj) > 0, > String _ when !((String) obj).isEmpty() > -> System.out.println("Positive number or non-empty string"); > default -> System.out.println("other"); > } > } > > Does it make sense to lift this restriction? Probably it could be > useful to declare separate guards? Ideally it should be possible to be > able to declare a pattern variable, which is visible inside the > pattern-specific guard only (but not inside the rule body). > > Another confusing thing here: > > void test(Object obj) { > switch (obj) { > case Integer _, > String _ when !((String) obj).isEmpty() > -> System.out.println("Number or non-empty string"); > default -> System.out.println("other"); > } > } > > Now, the guard is applied even if obj is Integer (resulting in > ClassCastException). This is not quite evident from the code. We may > say that 'when' precedence is lower than ',' precedence, but people > may expect the opposite. Should not we reconsider this and make guard > a part of the lebel element, rather than the part of the whole label? > > With best regards, > Tagir Valeev. From brian.goetz at oracle.com Mon Aug 14 15:04:19 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Aug 2023 15:04:19 +0000 Subject: [patterns] Several patterns and guards In-Reply-To: References: Message-ID: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> While we could certainly do this, I think the cost-benefit runs in the wrong direction here. This sort of thing is better expressed as an if, and that?s fine. (I think you?ll agree that this example is a little bit contrived.). > On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: > > Hello! > > Currently, when the switch label contains several patterns, only one > guard could be declared, which is applied to all the patterns at once. > In other words, the following code is not possible: > > void test(Object obj) { > switch (obj) { > case Integer _ when ((Integer) obj) > 0, > String _ when !((String) obj).isEmpty() > -> System.out.println("Positive number or non-empty string"); > default -> System.out.println("other"); > } > } > > Does it make sense to lift this restriction? Probably it could be > useful to declare separate guards? Ideally it should be possible to be > able to declare a pattern variable, which is visible inside the > pattern-specific guard only (but not inside the rule body). > > Another confusing thing here: > > void test(Object obj) { > switch (obj) { > case Integer _, > String _ when !((String) obj).isEmpty() > -> System.out.println("Number or non-empty string"); > default -> System.out.println("other"); > } > } > > Now, the guard is applied even if obj is Integer (resulting in > ClassCastException). This is not quite evident from the code. We may > say that 'when' precedence is lower than ',' precedence, but people > may expect the opposite. Should not we reconsider this and make guard > a part of the lebel element, rather than the part of the whole label? > > With best regards, > Tagir Valeev. From forax at univ-mlv.fr Mon Aug 14 15:14:18 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Aug 2023 17:14:18 +0200 (CEST) Subject: [patterns] Several patterns and guards In-Reply-To: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> Message-ID: <1294177029.12305077.1692026058558.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "Brian Goetz" > To: "Tagir Valeev" > Cc: "amber-spec-experts" > Sent: Monday, August 14, 2023 5:04:19 PM > Subject: Re: [patterns] Several patterns and guards > While we could certainly do this, I think the cost-benefit runs in the wrong > direction here. This sort of thing is better expressed as an if, and that?s > fine. (I think you?ll agree that this example is a little bit contrived.). And also the formatting does not help, you can compare void test(Object obj) { switch (obj) { case Integer _, String _ when !((String) obj).isEmpty() -> System.out.println("Number or non-empty string"); default -> System.out.println("other"); } } with void test(Object obj) { switch (obj) { case Integer _, String _ when !((String) obj).isEmpty() -> System.out.println("Number or non-empty string"); default -> System.out.println("other"); } } I think that if a comma is used in a "case", the "when" should be moved to the next line to make the semantics more obvious. R?mi > >> On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: >> >> Hello! >> >> Currently, when the switch label contains several patterns, only one >> guard could be declared, which is applied to all the patterns at once. >> In other words, the following code is not possible: >> >> void test(Object obj) { >> switch (obj) { >> case Integer _ when ((Integer) obj) > 0, >> String _ when !((String) obj).isEmpty() >> -> System.out.println("Positive number or non-empty string"); >> default -> System.out.println("other"); >> } >> } >> >> Does it make sense to lift this restriction? Probably it could be >> useful to declare separate guards? Ideally it should be possible to be >> able to declare a pattern variable, which is visible inside the >> pattern-specific guard only (but not inside the rule body). >> >> Another confusing thing here: >> >> void test(Object obj) { >> switch (obj) { >> case Integer _, >> String _ when !((String) obj).isEmpty() >> -> System.out.println("Number or non-empty string"); >> default -> System.out.println("other"); >> } >> } >> >> Now, the guard is applied even if obj is Integer (resulting in >> ClassCastException). This is not quite evident from the code. We may >> say that 'when' precedence is lower than ',' precedence, but people >> may expect the opposite. Should not we reconsider this and make guard >> a part of the lebel element, rather than the part of the whole label? >> >> With best regards, > > Tagir Valeev. From amaembo at gmail.com Mon Aug 14 15:28:19 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 14 Aug 2023 17:28:19 +0200 Subject: [patterns] Several patterns and guards In-Reply-To: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> Message-ID: I don't think that my example is contrived. Let's think of it from another angle. Are multiple patterns in the same switch label useful or contrived? If not useful, then let's disable them completely. If useful, then the next question: are guards with multiple patterns useful or contrived? Can you imagine a case when the single guard for the whole label will be useful (provided that we cannot declare any variables)? I'm not quite sure that a non-contrived example is possible. So probably we should disable guards with multiple patterns completely? My point is that having a separate guard per pattern is much more useful and less confusing than having a single guard for all patterns. You may convince me that I'm wrong by providing not-so-contrived examples. By the way, the following colon-style switch looks supported (though buggy again, reported JDK-8314226): void test(Object obj) { switch (obj) { case Integer _ when ((Integer) obj) > 0: case String _ when !((String) obj).isEmpty(): System.out.println(obj + ": Positive number or non-empty string"); break; default: System.out.println("other"); } } And it looks like there's no way to express the same with an arrow-style switch, or even replace adjacent cases with comma (which is possible for other cases). With best regards, Tagir Valeev. On Mon, Aug 14, 2023 at 5:04?PM Brian Goetz wrote: > > While we could certainly do this, I think the cost-benefit runs in the wrong direction here. This sort of thing is better expressed as an if, and that?s fine. (I think you?ll agree that this example is a little bit contrived.). > > > On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: > > > > Hello! > > > > Currently, when the switch label contains several patterns, only one > > guard could be declared, which is applied to all the patterns at once. > > In other words, the following code is not possible: > > > > void test(Object obj) { > > switch (obj) { > > case Integer _ when ((Integer) obj) > 0, > > String _ when !((String) obj).isEmpty() > > -> System.out.println("Positive number or non-empty string"); > > default -> System.out.println("other"); > > } > > } > > > > Does it make sense to lift this restriction? Probably it could be > > useful to declare separate guards? Ideally it should be possible to be > > able to declare a pattern variable, which is visible inside the > > pattern-specific guard only (but not inside the rule body). > > > > Another confusing thing here: > > > > void test(Object obj) { > > switch (obj) { > > case Integer _, > > String _ when !((String) obj).isEmpty() > > -> System.out.println("Number or non-empty string"); > > default -> System.out.println("other"); > > } > > } > > > > Now, the guard is applied even if obj is Integer (resulting in > > ClassCastException). This is not quite evident from the code. We may > > say that 'when' precedence is lower than ',' precedence, but people > > may expect the opposite. Should not we reconsider this and make guard > > a part of the lebel element, rather than the part of the whole label? > > > > With best regards, > > Tagir Valeev. > From amaembo at gmail.com Mon Aug 14 15:31:59 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 14 Aug 2023 17:31:59 +0200 Subject: [patterns] Several patterns and guards In-Reply-To: <1294177029.12305077.1692026058558.JavaMail.zimbra@univ-eiffel.fr> References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> <1294177029.12305077.1692026058558.JavaMail.zimbra@univ-eiffel.fr> Message-ID: > And also the formatting does not help, you can compare Of course, if you know the confusing semantics of the language, you can format the code to make it less confusing. We can also provide IDE warnings or autoformatting to help the users to avoid confusion. But it's always better to make the language non-confusing in the first place. With best regards, Tagir Valeev. > > void test(Object obj) { > switch (obj) { > case Integer _, > String _ when !((String) obj).isEmpty() > -> System.out.println("Number or non-empty string"); > default -> System.out.println("other"); > } > } > > with > > void test(Object obj) { > switch (obj) { > case Integer _, String _ > when !((String) obj).isEmpty() > -> System.out.println("Number or non-empty string"); > default -> System.out.println("other"); > } > } > > I think that if a comma is used in a "case", the "when" should be moved to the next line to make the semantics more obvious. > > R?mi > > > > >> On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: > >> > >> Hello! > >> > >> Currently, when the switch label contains several patterns, only one > >> guard could be declared, which is applied to all the patterns at once. > >> In other words, the following code is not possible: > >> > >> void test(Object obj) { > >> switch (obj) { > >> case Integer _ when ((Integer) obj) > 0, > >> String _ when !((String) obj).isEmpty() > >> -> System.out.println("Positive number or non-empty string"); > >> default -> System.out.println("other"); > >> } > >> } > >> > >> Does it make sense to lift this restriction? Probably it could be > >> useful to declare separate guards? Ideally it should be possible to be > >> able to declare a pattern variable, which is visible inside the > >> pattern-specific guard only (but not inside the rule body). > >> > >> Another confusing thing here: > >> > >> void test(Object obj) { > >> switch (obj) { > >> case Integer _, > >> String _ when !((String) obj).isEmpty() > >> -> System.out.println("Number or non-empty string"); > >> default -> System.out.println("other"); > >> } > >> } > >> > >> Now, the guard is applied even if obj is Integer (resulting in > >> ClassCastException). This is not quite evident from the code. We may > >> say that 'when' precedence is lower than ',' precedence, but people > >> may expect the opposite. Should not we reconsider this and make guard > >> a part of the lebel element, rather than the part of the whole label? > >> > >> With best regards, > > > Tagir Valeev. From forax at univ-mlv.fr Mon Aug 14 15:43:49 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 14 Aug 2023 17:43:49 +0200 (CEST) Subject: [patterns] Several patterns and guards In-Reply-To: References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> Message-ID: <920696704.12308848.1692027829361.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "Tagir Valeev" > To: "Brian Goetz" > Cc: "amber-spec-experts" > Sent: Monday, August 14, 2023 5:28:19 PM > Subject: Re: [patterns] Several patterns and guards > I don't think that my example is contrived. Let's think of it from > another angle. Are multiple patterns in the same switch label useful > or contrived? If not useful, then let's disable them completely. If > useful, then the next question: are guards with multiple patterns > useful or contrived? Can you imagine a case when the single guard for > the whole label will be useful (provided that we cannot declare any > variables)? I'm not quite sure that a non-contrived example is > possible. So probably we should disable guards with multiple patterns > completely? > > My point is that having a separate guard per pattern is much more > useful and less confusing than having a single guard for all patterns. > You may convince me that I'm wrong by providing not-so-contrived > examples. > > By the way, the following colon-style switch looks supported (though > buggy again, reported JDK-8314226): > > void test(Object obj) { > switch (obj) { > case Integer _ when ((Integer) obj) > 0: > case String _ when !((String) obj).isEmpty(): > System.out.println(obj + ": Positive number or non-empty string"); > break; > default: > System.out.println("other"); > } > } > > And it looks like there's no way to express the same with an > arrow-style switch, or even replace adjacent cases with comma (which > is possible for other cases). We know that at some point, we will need to lift any methods to a deconstruction pattern, this will solve this kind of example. static pattern (Integer) isStrictlyPositive(Integer value) { if (value > 0) { return match value; } return nomatch; } static pattern () isEmpty(String s) { if (s.isEmpty()) { return match; } return nomatch; } void test(Object obj) { switch (obj) { case Utils::isStrictlyPositive _, Utils::isEmpty -> System.out.println(obj + ": Positive number or non-empty string"); default -> System.out.println("other"); } } > > With best regards, > Tagir Valeev. regards, R?mi > > On Mon, Aug 14, 2023 at 5:04?PM Brian Goetz wrote: >> >> While we could certainly do this, I think the cost-benefit runs in the wrong >> direction here. This sort of thing is better expressed as an if, and that?s >> fine. (I think you?ll agree that this example is a little bit contrived.). >> >> > On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: >> > >> > Hello! >> > >> > Currently, when the switch label contains several patterns, only one >> > guard could be declared, which is applied to all the patterns at once. >> > In other words, the following code is not possible: >> > >> > void test(Object obj) { >> > switch (obj) { >> > case Integer _ when ((Integer) obj) > 0, >> > String _ when !((String) obj).isEmpty() >> > -> System.out.println("Positive number or non-empty string"); >> > default -> System.out.println("other"); >> > } >> > } >> > >> > Does it make sense to lift this restriction? Probably it could be >> > useful to declare separate guards? Ideally it should be possible to be >> > able to declare a pattern variable, which is visible inside the >> > pattern-specific guard only (but not inside the rule body). >> > >> > Another confusing thing here: >> > >> > void test(Object obj) { >> > switch (obj) { >> > case Integer _, >> > String _ when !((String) obj).isEmpty() >> > -> System.out.println("Number or non-empty string"); >> > default -> System.out.println("other"); >> > } >> > } >> > >> > Now, the guard is applied even if obj is Integer (resulting in >> > ClassCastException). This is not quite evident from the code. We may >> > say that 'when' precedence is lower than ',' precedence, but people >> > may expect the opposite. Should not we reconsider this and make guard >> > a part of the lebel element, rather than the part of the whole label? >> > >> > With best regards, >> > Tagir Valeev. From brian.goetz at oracle.com Mon Aug 14 15:44:37 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Aug 2023 15:44:37 +0000 Subject: [patterns] Several patterns and guards In-Reply-To: References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> Message-ID: So, there are two stable points here: make the where clause part of the pattern, or match it with the case label entirely. The middle option is not stable; a case like case P, Q when e: Is no longer clear, and worse, if the when clause binds to Q, then we would have to repeat the guard if we wanted `(P|Q) when e`. We explored guards on patterns (P && e) and concluded that it was better treated as part of the switch rather than the pattern. > On Aug 14, 2023, at 8:28 AM, Tagir Valeev wrote: > > I don't think that my example is contrived. Let's think of it from > another angle. Are multiple patterns in the same switch label useful > or contrived? If not useful, then let's disable them completely. If > useful, then the next question: are guards with multiple patterns > useful or contrived? Can you imagine a case when the single guard for > the whole label will be useful (provided that we cannot declare any > variables)? I'm not quite sure that a non-contrived example is > possible. So probably we should disable guards with multiple patterns > completely? > > My point is that having a separate guard per pattern is much more > useful and less confusing than having a single guard for all patterns. > You may convince me that I'm wrong by providing not-so-contrived > examples. > > By the way, the following colon-style switch looks supported (though > buggy again, reported JDK-8314226): > > void test(Object obj) { > switch (obj) { > case Integer _ when ((Integer) obj) > 0: > case String _ when !((String) obj).isEmpty(): > System.out.println(obj + ": Positive number or non-empty string"); > break; > default: > System.out.println("other"); > } > } > > And it looks like there's no way to express the same with an > arrow-style switch, or even replace adjacent cases with comma (which > is possible for other cases). > > With best regards, > Tagir Valeev. > > On Mon, Aug 14, 2023 at 5:04?PM Brian Goetz wrote: >> >> While we could certainly do this, I think the cost-benefit runs in the wrong direction here. This sort of thing is better expressed as an if, and that?s fine. (I think you?ll agree that this example is a little bit contrived.). >> >>> On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: >>> >>> Hello! >>> >>> Currently, when the switch label contains several patterns, only one >>> guard could be declared, which is applied to all the patterns at once. >>> In other words, the following code is not possible: >>> >>> void test(Object obj) { >>> switch (obj) { >>> case Integer _ when ((Integer) obj) > 0, >>> String _ when !((String) obj).isEmpty() >>> -> System.out.println("Positive number or non-empty string"); >>> default -> System.out.println("other"); >>> } >>> } >>> >>> Does it make sense to lift this restriction? Probably it could be >>> useful to declare separate guards? Ideally it should be possible to be >>> able to declare a pattern variable, which is visible inside the >>> pattern-specific guard only (but not inside the rule body). >>> >>> Another confusing thing here: >>> >>> void test(Object obj) { >>> switch (obj) { >>> case Integer _, >>> String _ when !((String) obj).isEmpty() >>> -> System.out.println("Number or non-empty string"); >>> default -> System.out.println("other"); >>> } >>> } >>> >>> Now, the guard is applied even if obj is Integer (resulting in >>> ClassCastException). This is not quite evident from the code. We may >>> say that 'when' precedence is lower than ',' precedence, but people >>> may expect the opposite. Should not we reconsider this and make guard >>> a part of the lebel element, rather than the part of the whole label? >>> >>> With best regards, >>> Tagir Valeev. >> From guy.steele at oracle.com Mon Aug 14 15:54:55 2023 From: guy.steele at oracle.com (Guy Steele) Date: Mon, 14 Aug 2023 15:54:55 +0000 Subject: [patterns] Several patterns and guards In-Reply-To: References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> Message-ID: <74016065-0F4D-4067-B1BB-E0409FA0A5F7@oracle.com> Or you could just require parentheses when the meaning might not be clear. And I mean _require_: allow case P when e: case P, (Q when e): case (P, Q) when e: but NOT case P, Q when e: And if you object that case (P, Q) when e: should really mean a single tuple pattern (P, Q) plus a guard on it (I agree!), then that tells me that it was a bad idea in the first place to use commas as a separator of patterns in a case label. ?Guy > On Aug 14, 2023, at 11:44 AM, Brian Goetz wrote: > > So, there are two stable points here: make the where clause part of the pattern, or match it with the case label entirely. The middle option is not stable; a case like > > case P, Q when e: > > Is no longer clear, and worse, if the when clause binds to Q, then we would have to repeat the guard if we wanted `(P|Q) when e`. We explored guards on patterns (P && e) and concluded that it was better treated as part of the switch rather than the pattern. > > > >> On Aug 14, 2023, at 8:28 AM, Tagir Valeev wrote: >> >> I don't think that my example is contrived. Let's think of it from >> another angle. Are multiple patterns in the same switch label useful >> or contrived? If not useful, then let's disable them completely. If >> useful, then the next question: are guards with multiple patterns >> useful or contrived? Can you imagine a case when the single guard for >> the whole label will be useful (provided that we cannot declare any >> variables)? I'm not quite sure that a non-contrived example is >> possible. So probably we should disable guards with multiple patterns >> completely? >> >> My point is that having a separate guard per pattern is much more >> useful and less confusing than having a single guard for all patterns. >> You may convince me that I'm wrong by providing not-so-contrived >> examples. >> >> By the way, the following colon-style switch looks supported (though >> buggy again, reported JDK-8314226): >> >> void test(Object obj) { >> switch (obj) { >> case Integer _ when ((Integer) obj) > 0: >> case String _ when !((String) obj).isEmpty(): >> System.out.println(obj + ": Positive number or non-empty string"); >> break; >> default: >> System.out.println("other"); >> } >> } >> >> And it looks like there's no way to express the same with an >> arrow-style switch, or even replace adjacent cases with comma (which >> is possible for other cases). >> >> With best regards, >> Tagir Valeev. >> >> On Mon, Aug 14, 2023 at 5:04?PM Brian Goetz wrote: >>> >>> While we could certainly do this, I think the cost-benefit runs in the wrong direction here. This sort of thing is better expressed as an if, and that?s fine. (I think you?ll agree that this example is a little bit contrived.). >>> >>>> On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: >>>> >>>> Hello! >>>> >>>> Currently, when the switch label contains several patterns, only one >>>> guard could be declared, which is applied to all the patterns at once. >>>> In other words, the following code is not possible: >>>> >>>> void test(Object obj) { >>>> switch (obj) { >>>> case Integer _ when ((Integer) obj) > 0, >>>> String _ when !((String) obj).isEmpty() >>>> -> System.out.println("Positive number or non-empty string"); >>>> default -> System.out.println("other"); >>>> } >>>> } >>>> >>>> Does it make sense to lift this restriction? Probably it could be >>>> useful to declare separate guards? Ideally it should be possible to be >>>> able to declare a pattern variable, which is visible inside the >>>> pattern-specific guard only (but not inside the rule body). >>>> >>>> Another confusing thing here: >>>> >>>> void test(Object obj) { >>>> switch (obj) { >>>> case Integer _, >>>> String _ when !((String) obj).isEmpty() >>>> -> System.out.println("Number or non-empty string"); >>>> default -> System.out.println("other"); >>>> } >>>> } >>>> >>>> Now, the guard is applied even if obj is Integer (resulting in >>>> ClassCastException). This is not quite evident from the code. We may >>>> say that 'when' precedence is lower than ',' precedence, but people >>>> may expect the opposite. Should not we reconsider this and make guard >>>> a part of the lebel element, rather than the part of the whole label? >>>> >>>> With best regards, >>>> Tagir Valeev. >>> > From amaembo at gmail.com Mon Aug 14 16:04:42 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 14 Aug 2023 18:04:42 +0200 Subject: [patterns] Several patterns and guards In-Reply-To: References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> Message-ID: Hello! On Mon, Aug 14, 2023 at 5:44?PM Brian Goetz wrote: > > So, there are two stable points here: make the where clause part of the pattern, or match it with the case label entirely. The middle option is not stable; a case like > > case P, Q when e: > > Is no longer clear Exactly, it's no longer clear. This is my point: this syntax is allowed now, and people may assume that 'when e' belongs to Q, rather than to both patterns. Probably my feeling of English is bad, but to me a comma looks like a stronger clause separator than 'when'. > and worse, if the when clause binds to Q, then we would have to repeat the guard if we wanted `(P|Q) when e`. But at least you could express this. Now, if you want a guard to be applied to Q only, you cannot express this at all. Your options are either to repeat the whole switch rule, or to fallback to a colon switch. > We explored guards on patterns (P && e) and concluded that it was better treated as part of the switch rather than the pattern. It's ok if it doesn't belong to a pattern (e.g. cannot appear in instanceof or inside a deconstruction). Still I'm not sure that the middle option (guard belongs to switch label _element_) was thoroughly explored. Or probably I've missed the discussion (in this case, I would be glad if you pointed me on it). Note that it was irrelevant until unnamed variables were introduced, as several patterns in the label were syntactically incorrect. With best regards, Tagir Valeev. > > > > > On Aug 14, 2023, at 8:28 AM, Tagir Valeev wrote: > > > > I don't think that my example is contrived. Let's think of it from > > another angle. Are multiple patterns in the same switch label useful > > or contrived? If not useful, then let's disable them completely. If > > useful, then the next question: are guards with multiple patterns > > useful or contrived? Can you imagine a case when the single guard for > > the whole label will be useful (provided that we cannot declare any > > variables)? I'm not quite sure that a non-contrived example is > > possible. So probably we should disable guards with multiple patterns > > completely? > > > > My point is that having a separate guard per pattern is much more > > useful and less confusing than having a single guard for all patterns. > > You may convince me that I'm wrong by providing not-so-contrived > > examples. > > > > By the way, the following colon-style switch looks supported (though > > buggy again, reported JDK-8314226): > > > > void test(Object obj) { > > switch (obj) { > > case Integer _ when ((Integer) obj) > 0: > > case String _ when !((String) obj).isEmpty(): > > System.out.println(obj + ": Positive number or non-empty string"); > > break; > > default: > > System.out.println("other"); > > } > > } > > > > And it looks like there's no way to express the same with an > > arrow-style switch, or even replace adjacent cases with comma (which > > is possible for other cases). > > > > With best regards, > > Tagir Valeev. > > > > On Mon, Aug 14, 2023 at 5:04?PM Brian Goetz wrote: > >> > >> While we could certainly do this, I think the cost-benefit runs in the wrong direction here. This sort of thing is better expressed as an if, and that?s fine. (I think you?ll agree that this example is a little bit contrived.). > >> > >>> On Aug 14, 2023, at 5:15 AM, Tagir Valeev wrote: > >>> > >>> Hello! > >>> > >>> Currently, when the switch label contains several patterns, only one > >>> guard could be declared, which is applied to all the patterns at once. > >>> In other words, the following code is not possible: > >>> > >>> void test(Object obj) { > >>> switch (obj) { > >>> case Integer _ when ((Integer) obj) > 0, > >>> String _ when !((String) obj).isEmpty() > >>> -> System.out.println("Positive number or non-empty string"); > >>> default -> System.out.println("other"); > >>> } > >>> } > >>> > >>> Does it make sense to lift this restriction? Probably it could be > >>> useful to declare separate guards? Ideally it should be possible to be > >>> able to declare a pattern variable, which is visible inside the > >>> pattern-specific guard only (but not inside the rule body). > >>> > >>> Another confusing thing here: > >>> > >>> void test(Object obj) { > >>> switch (obj) { > >>> case Integer _, > >>> String _ when !((String) obj).isEmpty() > >>> -> System.out.println("Number or non-empty string"); > >>> default -> System.out.println("other"); > >>> } > >>> } > >>> > >>> Now, the guard is applied even if obj is Integer (resulting in > >>> ClassCastException). This is not quite evident from the code. We may > >>> say that 'when' precedence is lower than ',' precedence, but people > >>> may expect the opposite. Should not we reconsider this and make guard > >>> a part of the lebel element, rather than the part of the whole label? > >>> > >>> With best regards, > >>> Tagir Valeev. > >> > From brian.goetz at oracle.com Tue Aug 15 16:04:01 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 15 Aug 2023 16:04:01 +0000 Subject: [patterns] Several patterns and guards In-Reply-To: References: <480A98E1-2A29-4C37-AB32-B33894977640@oracle.com> Message-ID: <27663DFF-A918-45C6-BEE4-417029920B20@oracle.com> > On Aug 14, 2023, at 9:04 AM, Tagir Valeev wrote: > > Hello! > > On Mon, Aug 14, 2023 at 5:44?PM Brian Goetz wrote: >> >> So, there are two stable points here: make the where clause part of the pattern, or match it with the case label entirely. The middle option is not stable; a case like >> >> case P, Q when e: >> >> Is no longer clear > > Exactly, it's no longer clear. This is my point: this syntax is > allowed now, and people may assume that 'when e' belongs to Q, rather > than to both patterns. Probably my feeling of English is bad, but to > me a comma looks like a stronger clause separator than 'when'. When we went back and forth over whether a guard is attached to a pattern or a case label (and we did this several times), I think we basically made this choice. When a construct is delimited with multiple keywords, the dependent keyword (else, while, when) is connected to its ancestor (if, do, case). So case ? when ? form a pair, and the syntax was chosen to reflect that. You are right that whichever way we do it, some people will be confused; `case P, Q when e` could be misunderstood because we?re using two features together. But I don?t think the answer is to try to cram yet more into switch labels; there?s already a lot (some would say too much) going on there already. If you want to use multiple patterns with different guards, you can split the patterns into multiple cases. > It's ok if it doesn't belong to a pattern (e.g. cannot appear in > instanceof or inside a deconstruction). Still I'm not sure that the > middle option (guard belongs to switch label _element_) was thoroughly > explored. Or probably I've missed the discussion (in this case, I > would be glad if you pointed me on it). Note that it was irrelevant > until unnamed variables were introduced, as several patterns in the > label were syntactically incorrect. At one point we also explored OR patterns, as in: case Foo(int x) || Bar(int x): In theory we could give x a meaning here, but people felt that this was too confusing. From amaembo at gmail.com Wed Aug 16 09:07:16 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Wed, 16 Aug 2023 11:07:16 +0200 Subject: [patterns] Multiple patterns without unnamed variables Message-ID: Hello! Just discovered a small discrepancy between spec and implementation in JDK 21. Consider the following code: public class Test { record R1() {} record R2() {} static void test(Object obj) { switch (obj) { case R1(), R2() -> System.out.println("R1 or R2"); default -> System.out.println("other"); } } public static void main(String[] args) { test(new R1()); } } It can be executed using java build 21-ea+27-2343 _without_ enabling preview: >\jdks\jdk-21\bin\java.exe Test.java R1 or R2 However, the spec draft for JDK 21 [1] doesn't allow several patterns in the same switch label. See the SwitchLabel production on the page 501: SwitchLabel: case CaseConstant {, CaseConstant} case null [, default] case CasePattern [Guard] // only one pattern is allowed! default Such code is legal under JEP-443 but it should require --enable-preview. On the other hand, I understand how it happens. Probably it would be better to update the JDK 21 spec to allow it? Or it should be considered as a compiler bug? With best regards, Tagir Valeev. [1] https://cr.openjdk.org/~iris/se/21/latestSpec/java-se-21-jls-pr-diffs.pdf From amaembo at gmail.com Fri Aug 18 09:48:09 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 18 Aug 2023 11:48:09 +0200 Subject: [patterns] Domination in guarded multiple pattern labels Message-ID: Hello! Consider the following code: public class Test { void test(Object obj) { switch (obj) { case Integer _, CharSequence _, String _ when obj.hashCode() > 0 -> { } default -> throw new IllegalStateException("Unexpected value: " + obj); } } } Javac compiles it, despite the `String _` pattern being unreachable. It looks like the spec [1] allows it. It speaks about domination of case labels, but not about the domination of individual patterns within the case label. I think this part should be improved. Namely, for several patterns under the single case label, domination rules must apply, and the guard must be ignored. It's interesting that if we remove the guard, the compilation fails: Test.java:4:45 java: this case label is dominated by a preceding case label While formally according to the spec, this should be accepted, as spec in its current shape says us only about 'preceding case label', and there's no preceding case label at all, so the first label should not be dominated. With best regards, Tagir Valeev [1] https://cr.openjdk.org/~abimpoudis/unnamed/jep443-20230322/specs/unnamed-jls.html#jls-14.11.1 From angelos.bimpoudis at oracle.com Sat Aug 19 14:00:09 2023 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Sat, 19 Aug 2023 14:00:09 +0000 Subject: [patterns] Domination in guarded multiple pattern labels In-Reply-To: References: Message-ID: Hello hello :-) I agree with you that both cases should not be valid. Regardless of the presence of a guard, if intra-case dominance is violated, the switch needs to be rejected. This is a compiler bug. Good catch ? I think, the intra-case dominance is covered by the following rule at the spec (pasting): It is a compile-time error if in a switch block there is a case label with case patterns p1,...,pn (n > 1) where one of the patterns pi (1 ? i < n) dominates another of the patterns pj (i < j ? n). Is this what you had in mind? Many thanks and best regards, Angelos ________________________________ From: amber-spec-experts on behalf of Tagir Valeev Sent: 18 August 2023 11:48 To: amber-spec-experts Subject: [patterns] Domination in guarded multiple pattern labels Hello! Consider the following code: public class Test { void test(Object obj) { switch (obj) { case Integer _, CharSequence _, String _ when obj.hashCode() > 0 -> { } default -> throw new IllegalStateException("Unexpected value: " + obj); } } } Javac compiles it, despite the `String _` pattern being unreachable. It looks like the spec [1] allows it. It speaks about domination of case labels, but not about the domination of individual patterns within the case label. I think this part should be improved. Namely, for several patterns under the single case label, domination rules must apply, and the guard must be ignored. It's interesting that if we remove the guard, the compilation fails: Test.java:4:45 java: this case label is dominated by a preceding case label While formally according to the spec, this should be accepted, as spec in its current shape says us only about 'preceding case label', and there's no preceding case label at all, so the first label should not be dominated. With best regards, Tagir Valeev [1] https://cr.openjdk.org/~abimpoudis/unnamed/jep443-20230322/specs/unnamed-jls.html#jls-14.11.1 -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Aug 19 19:17:20 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 19 Aug 2023 21:17:20 +0200 Subject: [patterns] Domination in guarded multiple pattern labels In-Reply-To: References: Message-ID: On Sat, Aug 19, 2023 at 4:00?PM Angelos Bimpoudis wrote: > It is a compile-time error if in a switch block there is a case label with case patterns p1,...,pn (n > 1) where one of the patterns pi (1 ? i < n) dominates another of the patterns pj (i < j ? n). Ah, indeed! My bad, I haven't read until this point, thanks. With best regards, Tagir Valeev. > > Is this what you had in mind? > > Many thanks and best regards, > Angelos > > ________________________________ > From: amber-spec-experts on behalf of Tagir Valeev > Sent: 18 August 2023 11:48 > To: amber-spec-experts > Subject: [patterns] Domination in guarded multiple pattern labels > > Hello! > > Consider the following code: > > public class Test { > void test(Object obj) { > switch (obj) { > case Integer _, CharSequence _, String _ when obj.hashCode() > 0 -> { > } > default -> throw new IllegalStateException("Unexpected > value: " + obj); > } > } > } > > Javac compiles it, despite the `String _` pattern being unreachable. > It looks like the spec [1] allows it. It speaks about domination of > case labels, but not about the domination of individual patterns > within the case label. I think this part should be improved. Namely, > for several patterns under the single case label, domination rules > must apply, and the guard must be ignored. > > It's interesting that if we remove the guard, the compilation fails: > > Test.java:4:45 > java: this case label is dominated by a preceding case label > > While formally according to the spec, this should be accepted, as spec > in its current shape says us only about 'preceding case label', and > there's no preceding case label at all, so the first label should not > be dominated. > > With best regards, > Tagir Valeev > > [1] https://cr.openjdk.org/~abimpoudis/unnamed/jep443-20230322/specs/unnamed-jls.html#jls-14.11.1