From brian.goetz at oracle.com Thu Oct 1 20:33:28 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 1 Oct 2020 16:33:28 -0400 Subject: Fwd: Records feedback In-Reply-To: References: Message-ID: <071ca0ed-62bd-5575-137a-7db953ae771a@oracle.com> Received on the -comments list. A similar comment was made in the comments of this SO issue: https://stackoverflow.com/questions/64131753/why-is-the-variable-arity-record-component-not-inferred-with-custom-constructor -------- Forwarded Message -------- Subject: Records feedback Date: Thu, 1 Oct 2020 15:09:38 -0500 From: David Aaron Kaye To: java-se-spec-comments at openjdk.java.net Good afternoon, I am very excited to see Records coming to Java. I have been playing around with them since JDK 14. I noticed that in the proposal, JDK-8246771 , it says ...if the canonical constructor is explicitly declared then its access modifier must provide at least as much access as the record class I have implemented a functional List: public sealed interface List permits Cons, Nil { } public record Cons(A head, List tail) implements List { } @SuppressWarnings("rawtypes") public record Nil() implements List { public static final Nil instance = new Nil(); private Nil() { } } I made the constructor private because I want this to be a singleton, accessed from instance, but the current proposal does not allow me to do that, and I get a compilation error on the private constructor. I could prefer to be able to make the constructor private so that I can have a record that is a singleton. Thanks, David Kaye -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Oct 1 21:24:12 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 1 Oct 2020 23:24:12 +0200 (CEST) Subject: Records feedback In-Reply-To: <071ca0ed-62bd-5575-137a-7db953ae771a@oracle.com> References: <071ca0ed-62bd-5575-137a-7db953ae771a@oracle.com> Message-ID: <29399735.787144.1601587452280.JavaMail.zimbra@u-pem.fr> Hi David, > De: "Brian Goetz" > ?: "amber-spec-experts" > Envoy?: Jeudi 1 Octobre 2020 22:33:28 > Objet: Fwd: Records feedback > Received on the -comments list. A Record does not support encapsulation (separating the public API and the private implementation) given that it's a named tuple. If you want encapsulation, use a class. public class Nil implements List { private static final Nil INSTANCE = new Nil(); private Nil() {} @SuppressWarnings("unchecked") public Nil nil() { return (Nil) INSTANCE; } } As a side note, i've also fixed your use of @SuppressWarnings("rawtypes") which should be used mostly when dealing with code written before Java 5. Here, you should use @SuppressWarnings("unchecked") to workaround the fact that there is no type Nothing in Java and no way to express that Nil is covariant at declaration site. regards, R?mi > -------- Forwarded Message -------- > Subject: Records feedback > Date: Thu, 1 Oct 2020 15:09:38 -0500 > From: David Aaron Kaye [ mailto:kayedavi at gmail.com | ] > To: [ mailto:java-se-spec-comments at openjdk.java.net | > java-se-spec-comments at openjdk.java.net ] > Good afternoon, > I am very excited to see Records coming to Java. I have been playing around > with them since JDK 14. I noticed that in the proposal, JDK-8246771 > [ https://bugs.openjdk.java.net/browse/JDK-8246771 | > ] , it says > ...if the canonical constructor is explicitly declared then its access > modifier must provide at least as much access as the record class > I have implemented a functional List: > public sealed interface List permits Cons, Nil { > } > public record Cons(A head, List tail) implements List { > } > @SuppressWarnings("rawtypes") > public record Nil() implements List { > public static final Nil instance = new Nil(); > private Nil() { > } > } > I made the constructor private because I want this to be a singleton, > accessed from instance, but the current proposal does not allow me to do > that, and I get a compilation error on the private constructor. I could > prefer to be able to make the constructor private so that I can have a > record that is a singleton. > Thanks, > David Kaye -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Oct 2 17:09:29 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 2 Oct 2020 13:09:29 -0400 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: Message-ID: <4349115a-df81-8c90-5d93-a36dd7939ff3@oracle.com> I think we're reaching diminishing returns here.? Here's how I would think about this: ?- We do an applicability check to see if the annotation _could apply_ to any of the things we are going to generate.? If the target isn't one of param/method/field/component, we issue an error. ?- If there is an annotation on a component that only applies to method / param, but the corresponding method/param is explicitly declare, oh well.? We promise to propagate well-formed annotations to all the places that it would apply, and "zero" is a valid value of all. On 9/28/2020 6:21 AM, Gavin Bierman wrote: > There?s one minor corner-case that I bring to your attention. Consider the > following: > > @Target(ElementType.METHOD) > @interface A { } > > record R(@A int x) { > int x() { return this.x; } > } > > The new rules ensure that this is an error as the annotation on the record > component is not propagated anywhere because of the explicit accessor > declaration. However, what if the accessor was annotated with the same > annotation? > > @Target(ElementType.METHOD) > @interface A { } > > record R(@A int x) { > @A int x() { return this.x; } > } > > As it stands, the spec rules this out as an error. For simple annotations, > equality is simple to define, but do we want to attempt to define it for all > kinds of annotations? This feels like it?s not worth the complexity, but I?d be > happy to hear opinions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Oct 5 04:47:05 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 5 Oct 2020 11:47:05 +0700 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: <4349115a-df81-8c90-5d93-a36dd7939ff3@oracle.com> References: <4349115a-df81-8c90-5d93-a36dd7939ff3@oracle.com> Message-ID: On Sat, Oct 3, 2020 at 12:09 AM Brian Goetz wrote: > > I think we're reaching diminishing returns here. Well, to me, it's not diminishing. It's certainly a piece of code that will not have any effect after compilation, so it's a mistake. Reporting it is not that hard. Having a class/runtime annotation in source code that disappears completely in the compiled class-file without any warning sounds confusing and may cause subtle problems. Well, of course, we can create an inspection in IntelliJ IDEA to warn about these cases, and people will like IntelliJ IDEA even more, but I believe it's a compiler job to reject this, like Java already does with e.g. unreachable code. With best regards, Tagir Valeev. From gavin.bierman at oracle.com Mon Oct 5 12:53:10 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 5 Oct 2020 13:53:10 +0100 Subject: [patterns-instanceof] Spec for next version of Patterns in instanceof JEP Message-ID: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> Dear all: A draft of the spec for the Patterns in instanceof feature that we plan to finalize in JDK 16 is now available: http://cr.openjdk.java.net/~gbierman/8250623/latest/ [NB: The URL will change once we have a JEP number, and will be announced.] The changes are the same as those in the second preview that was released in Java SE 16, except for minor editorial changes and the following: - To lift the restriction that pattern variables are implicitly final. This allows pattern variables to be considered as a strict subset of local variables. A number of simplifications to the spec result from this change. - To make it a compile-time error for an expression of type _S_ to be matched against a pattern of type _T_, where _S_ is a subtype of _T_. (This pattern match will always succeed and is then pointless. The opposite case, where a pattern match will always fail is already a compile-time error.) Comments welcome! Gavin From brian.goetz at oracle.com Mon Oct 5 15:20:05 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 5 Oct 2020 11:20:05 -0400 Subject: [patterns-instanceof] Spec for next version of Patterns in instanceof JEP In-Reply-To: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> References: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> Message-ID: <263d4891-725d-8a21-b1f3-00fab76818c0@oracle.com> Overall I really like the simplification of merging pattern variables into locals, just with more complex scopes.? The revised definition of effective finality is very natural.? This will all pay off further when we add pattern assignment statements. The obvious caveat is that you have to search the entire text for "local variable" and consider whether you need to qualify with "that is not a pattern variable".? I suspect you've already done this. In 4.12.3, the comment on DA sounds like it doesn't apply to pattern variables.? And strictly, it doesn't, but this would be a good place to mention that the scoping of a pattern variable coincides with where that variable would be DA.? I see this in 4.12.5 but a more general statement where you introduce pattern variables would be nice, even if it is a note rather than normative text. 5.5.? I think of `instanceof` as having two forms: ??? o instanceof Type ??? o instanceof Pattern This phrasing ("converted to the type indicated by the second operand") is consistent with the first, but I think needs a few more words to be consistent with the second?? Every pattern has a "principal type" (making up a term); the principal type of a type pattern `T t` is `T`; the principal type of a deconstruction pattern `D(...)` is `D`.? (The definition for other kinds of patterns like constant patterns or declared patterns are straightforward enough to define as needed.) (I assume the stuff on scoping and operational semantics of pattern matching is unchanged, so I skimmed that.) The shadowing rules still leave room for "flow refinement" as a later feature, where you have a local: ??? Object p; ??? if (p instanceof Point p) { ... } where the latter p is considered a type refinement of the former.? We've discussed this before, and it seems a reasonable feature to leave on the "maybe leave room for later" list, but we surely don't have to do anything about it now. I have one concern: > The second condition eliminates pointless instances of pattern > matching where the expression will always match the pattern. In > particular, the|null|literal is not compatible with any type test pattern. I am not sure I agree with this framing.? We need to separate the semantics of _patterns_ from the semantics of `instanceof`. A total pattern (var x, Object x, total type pattern) _does_ match null.? We may exclude some patterns from `instanceof`, but that doesn't change their semantics.? I think we should get ahead of this, rather than plan on patching it later. Even more so: > The null reference value does not match any type test pattern. According to the discussions surrounding switch, this is not true.? The null reference value matches any type pattern _that is total on the static type of the operand_.? Again, we've got to separate the semantics of the type pattern, from what `instanceof` does with a type pattern.? We get several shots at null before we actually match the pattern: we can eliminate some matches statically at compile time, and we can eliminate others by giving semantics to instanceof before we ask "does it match the pattern."? So we should be careful in which of these three buckets we put our null hostility. Overall, though, I am super-happy with how this spec has landed.? At the beginning, we we a bit fearful of how intrusive it would be; in the end, there is basically just the obvious set of new sections: pattern syntax, pattern scoping, pattern operational semantics.? That the diffs are almost entirely "adding new sections" looks like winning to me.? Further, looking ahead, adding both new kinds of patterns (e.g., deconstruction patterns) and new places that use patterns (e.g., pattern assignment) will have an obvious place to go. On 10/5/2020 8:53 AM, Gavin Bierman wrote: > Dear all: > > A draft of the spec for the Patterns in instanceof feature that we plan to > finalize in JDK 16 is now available: > > http://cr.openjdk.java.net/~gbierman/8250623/latest/ > > [NB: The URL will change once we have a JEP number, and will be announced.] > > The changes are the same as those in the second preview that was released in > Java SE 16, except for minor editorial changes and the following: > > - To lift the restriction that pattern variables are implicitly final. This > allows pattern variables to be considered as a strict subset of local > variables. A number of simplifications to the spec result from this change. > > - To make it a compile-time error for an expression of type _S_ to be matched > against a pattern of type _T_, where _S_ is a subtype of _T_. (This pattern > match will always succeed and is then pointless. The opposite case, where a > pattern match will always fail is already a compile-time error.) > > Comments welcome! > > Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Thu Oct 8 08:56:42 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 8 Oct 2020 09:56:42 +0100 Subject: [patterns-instanceof] Spec for next version of Patterns in instanceof JEP In-Reply-To: <263d4891-725d-8a21-b1f3-00fab76818c0@oracle.com> References: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> <263d4891-725d-8a21-b1f3-00fab76818c0@oracle.com> Message-ID: > On 5 Oct 2020, at 16:20, Brian Goetz wrote: > > > I have one concern: > >> The second condition eliminates pointless instances of pattern matching where the expression will always match the pattern. In particular, the null literal is not compatible with any type test pattern. > > I am not sure I agree with this framing. We need to separate the semantics of _patterns_ from the semantics of `instanceof`. A total pattern (var x, Object x, total type pattern) _does_ match null. We may exclude some patterns from `instanceof`, but that doesn't change their semantics. I think we should get ahead of this, rather than plan on patching it later. Yes, I had spotted this. Apologies, I put the error condition in the wrong section - in matching rather than in instanceof - although it?s effect right now is the same. I shall move it. > Even more so: > >> The null reference value does not match any type test pattern. > > According to the discussions surrounding switch, this is not true. The null reference value matches any type pattern _that is total on the static type of the operand_. Again, we've got to separate the semantics of the type pattern, from what `instanceof` does with a type pattern. We get several shots at null before we actually match the pattern: we can eliminate some matches statically at compile time, and we can eliminate others by giving semantics to instanceof before we ask "does it match the pattern." So we should be careful in which of these three buckets we put our null hostility. Yes, spotted this also! I have refactored the semantics. In fact, for now we don?t need to give a case about null reference value matching, as it is caught by the instanceof rules. > Overall, though, I am super-happy with how this spec has landed. At the beginning, we we a bit fearful of how intrusive it would be; in the end, there is basically just the obvious set of new sections: pattern syntax, pattern scoping, pattern operational semantics. That the diffs are almost entirely "adding new sections" looks like winning to me. Further, looking ahead, adding both new kinds of patterns (e.g., deconstruction patterns) and new places that use patterns (e.g., pattern assignment) will have an obvious place to go. Thanks. For now section 14.30 looks a little ?over-structured?, but it was carefully designed to be able to grow gracefully. Now this is JEP 394 (yay!), I will build a new version of the spec and put it in a more memorable place shortly. I will announce on this list when it is available. Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Thu Oct 8 09:18:45 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 8 Oct 2020 10:18:45 +0100 Subject: [patterns-instanceof] Spec for next version of Patterns in instanceof JEP In-Reply-To: <263d4891-725d-8a21-b1f3-00fab76818c0@oracle.com> References: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> <263d4891-725d-8a21-b1f3-00fab76818c0@oracle.com> Message-ID: <6614E6B3-6D27-45B3-AF64-3F85A313BB94@oracle.com> > On 5 Oct 2020, at 16:20, Brian Goetz wrote: > > 5.5. I think of `instanceof` as having two forms: > > o instanceof Type > o instanceof Pattern > > This phrasing ("converted to the type indicated by the second operand") is consistent with the first, but I think needs a few more words to be consistent with the second? Every pattern has a "principal type" (making up a term); the principal type of a type pattern `T t` is `T`; the principal type of a deconstruction pattern `D(...)` is `D`. (The definition for other kinds of patterns like constant patterns or declared patterns are straightforward enough to define as needed.) [The spec also thinks there are two forms of instanceof - see second line of 15.20.2!] You don?t need to make up a term; it?s there already. In 14.30.1.1.: The type of a type test pattern is the ReferenceType. I use this notion of ?type of a pattern? elsewhere. I tried to hedge the phrasing in 5.5 to spare myself mentioning both forms of instanceof, but perhaps it would just be clearer to do so. Thanks. Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Fri Oct 9 10:11:48 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 9 Oct 2020 12:11:48 +0200 (CEST) Subject: instanceof and exceptions Message-ID: <1967377838.977607.1602238308201.JavaMail.zimbra@u-pem.fr> Following the course on exceptions, where i explain that a catch() is an instanceof, two different students ask me why catch() can use '|' in between the exception types but instanceof can not. i.e why this code works try { ... } catch(RuntimeException | Error e) { throw e; } catch(Throwable t) { ... } but this one doesn't try { ... } catch(Throwable t) { if (t instanceof RuntimeException | Error e) { throw e; } ... } I wonder if people will want to do pattern matching on exceptions ? R?mi From gavin.bierman at oracle.com Fri Oct 9 10:31:59 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 9 Oct 2020 11:31:59 +0100 Subject: instanceof and exceptions In-Reply-To: <1967377838.977607.1602238308201.JavaMail.zimbra@u-pem.fr> References: <1967377838.977607.1602238308201.JavaMail.zimbra@u-pem.fr> Message-ID: <4EC8C518-05AB-4B87-B558-715973FAD265@oracle.com> > On 9 Oct 2020, at 11:11, Remi Forax wrote: > > Following the course on exceptions, where i explain that a catch() is an instanceof, > two different students ask me why catch() can use '|' in between the exception types but instanceof can not. > > i.e why this code works > try { > ... > } catch(RuntimeException | Error e) { > throw e; > } catch(Throwable t) { > ... > } > > but this one doesn't > try { > ... > } catch(Throwable t) { > if (t instanceof RuntimeException | Error e) { > throw e; > } > ... > } > > I wonder if people will want to do pattern matching on exceptions ? I?m sure they will! (We mentioned adding pattern matching to catch in our overview document https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html ) The issue - as always - is how we reconcile the syntactic choices that have already been made, and the ones that are natural for our more general setting of pattern matching. Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Oct 9 14:59:27 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 9 Oct 2020 10:59:27 -0400 Subject: instanceof and exceptions In-Reply-To: <1967377838.977607.1602238308201.JavaMail.zimbra@u-pem.fr> References: <1967377838.977607.1602238308201.JavaMail.zimbra@u-pem.fr> Message-ID: There's a simple (though unsatisfying) answer to your student's question: because catch is special.? (But this answer belies an important point: ad-hoc syntactic "patches" like multi-catch may be satisfying in the short term, but they always beget a flurry of "for consistency" arguments, which can often not be consistently satisfied.) Essentially, you are asking whether (a) OR patterns make sense, and (b) whether we can reuse the syntax A|B for OR'ed type patterns.? Let's pull on that string for a bit. Suppose that `(T|U) tu` were a type pattern.? The type test is clear enough, but what is the type of `tu`?? There are two candidates; a union type and LUB.? (Union types are clearly more general, but what does multi-catch do?? It does LUB.? See JLS 14.20.)? I suspect your preferred answer is "union type", since that's a more information-preserving answer.? One downside here is it provides a(nother) vector for non-denotable weird types to escape to where users can observe them.? (We faced a similar issue in Lambda (and LVTI): both allowed intersection types to escape more freely into the wild, and both had consequences.) It's a possibility, but I'm not sure it clears the bar for risk/reward. A more general answer that doesn't involve introducing new syntactic forms into the language would be to dust off the proposal for binding variable merging: ??? if (x instanceof RuntimeException e || x instanceof Error e) { X } Our original design allowed for this but we backed off because it was unclear whether the return-on-complexity was justified. Here, we'd see that we have two patterns, each with a binding of the same name, and exactly one of which can produce a binding at X.? So we can give `e` either a LUB or union type, just as with the "shortcut" T|U, but its a more general solution, and doesn't tease users with a "fake" union type syntax. Note that the "merging" situation has another analogue with the same semantics: fallthrough.? The above is like: ??? switch (x) { ??????? case RuntimeException e: ??????? case Error e: ??????????? X; ??? } If fake union types are not great, what about real union types? Well, it's been shown to be possible (https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.158.1253&rep=rep1&type=pdf), but its a lot; it seems like the cost-benefit isn't there either. Overall, I'm skeptical that the cost/benefit of union type patterns is positive, but they are doable. On 10/9/2020 6:11 AM, Remi Forax wrote: > Following the course on exceptions, where i explain that a catch() is an instanceof, > two different students ask me why catch() can use '|' in between the exception types but instanceof can not. > > i.e why this code works > try { > ... > } catch(RuntimeException | Error e) { > throw e; > } catch(Throwable t) { > ... > } > > but this one doesn't > try { > ... > } catch(Throwable t) { > if (t instanceof RuntimeException | Error e) { > throw e; > } > ... > } > > I wonder if people will want to do pattern matching on exceptions ? > > R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Oct 9 15:16:19 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 9 Oct 2020 11:16:19 -0400 Subject: Relaxed assignment conversions for sealed types Message-ID: Here's an idea that I've been thinking about for a few days, it's not urgent to decide on now, but I think it is worth considering in the background. When we did expression switch, we had an interesting discussion about what is the point of not writing a default clause on an optimistically total enum switch (and the same reasoning applies on switches on sealed types.)? Suppose I have: ??? var x = switch (trafficLight) { ??????? case RED -> ... ??????? case YELLOW -> ... ??????? case GREEN -> ... ??? } People like this because they don't have to write a silly default clause that just throws an silly exception with a silly message (and as a bonus, is hard to cover with tests.)? But Kevin pointed out that this is really the lesser benefit of the compiler reasoning about exhaustiveness; the greater benefit is that it allows you to more precisely capture assumptions in your program about totality, which the compiler can validate for you.? If later, someone adds BLUE to traffic lights, the above switch fails to recompile, and we are constructively informed about an assumption being violated, whereas if we had a default clause, the fact that our assumption went stale gets swept under the rug. I was writing some code with sealed classes the other day, and I discovered an analogue of this which we may want to consider.? I had: ??? public sealed interface Foo ??????? permits MyFooImpl { } ??? private class MyFooImpl implements Foo { } which I think we can agree will be a common enough pattern.? And I found myself wanting to write: ??? void m(Foo f) { ??????? MyFooImpl mfi = (MyFooImpl) f; ??????? ... ??? } This line of code is based on the assumption that Foo is sealed to permit only MyFooImpl, which is a valid assumption right now, since all this code exists only on my workstation.? But some day, someone else may extend Foo to permit two private implementations, but may not be aware of the time bombs I've buried here. Suppose, though, that U were assignable to T if U is a sealed type and all permitted subtypes of U are assignable to T.? Then I'd be able to write: ??? MyFooImpl mfi = f; Not only do I not have to write the cast (the minor benefit), but rather than burying the assumption "all implementations of Foo are castable to MyFooImpl" in implementation code that can only fail at runtime, I can capture it in a way the compiler can verify on every recompilation, and when the underlying assumption is invalidated, so is the code that makes the assumption.? This seems less brittle (the major benefit.) This generalizes, of course.? Suppose we have: ??? sealed interface X permits A, B { } ??? class A extends Base implements X { } ??? class B extends Base implements X { } Then X becomes assignable to Base. I'm not quite sure yet how to feel about this, but I really do like the idea of being able to put the assumptions like "X must be a Y" -- which people _will_ make -- in a place where the compiler can typecheck it. -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Oct 10 03:24:27 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 10 Oct 2020 10:24:27 +0700 Subject: instanceof and exceptions In-Reply-To: References: <1967377838.977607.1602238308201.JavaMail.zimbra@u-pem.fr> Message-ID: Hello! I don't like the idea of merging now, even though I advocated for it before. First, it creates several declaration sites for the same variable. It will complicate the navigation in IDE for users and for IDE authors as well. We have a similar case when the same method is declared in two interfaces and the user wants to navigate from the call-site to the method declaration (in this case IDE goes to one of the declarations). But in that case, there are actually two super-methods. And here we have one variable with several declarations. Second, it could be really hard to understand the type of the variable because it could be scattered over many expressions. Moreover, this may cause accidental reuse of the same name. And this opens a way for context-dependent types. E.g. consider the statement: if((obj instanceof A x && testA(x)) || (obj instanceof B x && testB(x))) useAorB(x); Here the type if `x` will depend on the place where it's used. I think we don't like to go to this direction. That said, I like OR patterns. I think having a LUB type for the pattern variable is completely ok for now. It's consistent with catch and useful enough. In IntelliJ IDEA we have quite many code patterns that can benefit from this. E.g. (just did quick structural search through the codebase): final PsiElement element = descriptor.getPsiElement(); if (element instanceof PsiNewExpression || element instanceof PsiArrayInitializerExpression) { PsiReplacementUtil.replaceExpression((PsiExpression)element, myEnumName + ".values()"); } Could be replaced with if (descriptor.getPsiElement() instanceof (PsiNewExpression | PsiArrayInitializerExpression) expression) { PsiReplacementUtil.replaceExpression(expression, myEnumName + ".values()"); } Or Object normalized = value; if (value instanceof Byte || value instanceof Short) { normalized = ((Number)value).intValue(); } Could be replaced with if (value instanceof (Byte | Short) number) { normalized = number.intValue(); } And so on. This allows reducing the number of typecasts even further. With best regards, Tagir Valeev. ??, 9 ???. 2020 ?., 21:59 Brian Goetz : > There's a simple (though unsatisfying) answer to your student's question: > because catch is special. (But this answer belies an important point: > ad-hoc syntactic "patches" like multi-catch may be satisfying in the short > term, but they always beget a flurry of "for consistency" arguments, which > can often not be consistently satisfied.) > > Essentially, you are asking whether (a) OR patterns make sense, and (b) > whether we can reuse the syntax A|B for OR'ed type patterns. Let's pull on > that string for a bit. > > Suppose that `(T|U) tu` were a type pattern. The type test is clear > enough, but what is the type of `tu`? There are two candidates; a union > type and LUB. (Union types are clearly more general, but what does > multi-catch do? It does LUB. See JLS 14.20.) I suspect your preferred > answer is "union type", since that's a more information-preserving answer. > One downside here is it provides a(nother) vector for non-denotable weird > types to escape to where users can observe them. (We faced a similar issue > in Lambda (and LVTI): both allowed intersection types to escape more freely > into the wild, and both had consequences.) It's a possibility, but I'm not > sure it clears the bar for risk/reward. > > A more general answer that doesn't involve introducing new syntactic forms > into the language would be to dust off the proposal for binding variable > merging: > > if (x instanceof RuntimeException e || x instanceof Error e) { X } > > Our original design allowed for this but we backed off because it was > unclear whether the return-on-complexity was justified. Here, we'd see > that we have two patterns, each with a binding of the same name, and > exactly one of which can produce a binding at X. So we can give `e` either > a LUB or union type, just as with the "shortcut" T|U, but its a more > general solution, and doesn't tease users with a "fake" union type syntax. > > Note that the "merging" situation has another analogue with the same > semantics: fallthrough. The above is like: > > switch (x) { > case RuntimeException e: > case Error e: > X; > } > > If fake union types are not great, what about real union types? Well, > it's been shown to be possible ( > https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.158.1253&rep=rep1&type=pdf), > but its a lot; it seems like the cost-benefit isn't there either. > > Overall, I'm skeptical that the cost/benefit of union type patterns is > positive, but they are doable. > > > > > > On 10/9/2020 6:11 AM, Remi Forax wrote: > > Following the course on exceptions, where i explain that a catch() is an instanceof, > two different students ask me why catch() can use '|' in between the exception types but instanceof can not. > > i.e why this code works > try { > ... > } catch(RuntimeException | Error e) { > throw e; > } catch(Throwable t) { > ... > } > > but this one doesn't > try { > ... > } catch(Throwable t) { > if (t instanceof RuntimeException | Error e) { > throw e; > } > ... > } > > I wonder if people will want to do pattern matching on exceptions ? > > R?mi > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Oct 10 11:10:10 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sat, 10 Oct 2020 13:10:10 +0200 (CEST) Subject: instanceof and exceptions In-Reply-To: References: <1967377838.977607.1602238308201.JavaMail.zimbra@u-pem.fr> Message-ID: <1103474810.1512217.1602328210351.JavaMail.zimbra@u-pem.fr> Like Tagir said, i'm not a fan of having two declarations for one variable too. > Suppose that `(T|U) tu` were a type pattern. The type test is clear enough, but > what is the type of `tu`? There are two candidates; a union type and LUB. > (Union types are clearly more general, but what does multi-catch do? It does > LUB. See JLS 14.20.) I suspect your preferred answer is "union type", since > that's a more information-preserving answer. Technically, it's not fully LUB, because you have the precise re-throw, so it's more it's an union type but it's LUB when you call a method with the exception as argument or call a method on the exception. So we may be able to come with a similar semantics for the pattern A | B, this is an union type but it's LUB when ... . With the constraint that the semantics has to be retro-compatible in case of A and B are exceptions. > One downside here is it provides a(nother) vector for non-denotable weird types > to escape to where users can observe them. (We faced a similar issue in Lambda > (and LVTI): both allowed intersection types to escape more freely into the > wild, and both had consequences.) It's a possibility, but I'm not sure it > clears the bar for risk/reward. yes, I don't think we have to worry too much in this case because as explained above once you do a method call, A | B is erased to LUB. So if we introduce a union type, it will be kept local to a method. R?mi > De: "Tagir Valeev" > ?: "Brian Goetz" > Cc: "Remi Forax" , "amber-spec-experts" > > Envoy?: Samedi 10 Octobre 2020 05:24:27 > Objet: Re: instanceof and exceptions > Hello! > I don't like the idea of merging now, even though I advocated for it before. > First, it creates several declaration sites for the same variable. It will > complicate the navigation in IDE for users and for IDE authors as well. We have > a similar case when the same method is declared in two interfaces and the user > wants to navigate from the call-site to the method declaration (in this case > IDE goes to one of the declarations). But in that case, there are actually two > super-methods. And here we have one variable with several declarations. Second, > it could be really hard to understand the type of the variable because it could > be scattered over many expressions. Moreover, this may cause accidental reuse > of the same name. And this opens a way for context-dependent types. E.g. > consider the statement: > if((obj instanceof A x && testA(x)) || (obj instanceof B x && testB(x))) > useAorB(x); > Here the type if `x` will depend on the place where it's used. I think we don't > like to go to this direction. > That said, I like OR patterns. I think having a LUB type for the pattern > variable is completely ok for now. It's consistent with catch and useful > enough. In IntelliJ IDEA we have quite many code patterns that can benefit from > this. E.g. (just did quick structural search through the codebase): > final PsiElement element = descriptor.getPsiElement(); > if (element instanceof PsiNewExpression || element instanceof > PsiArrayInitializerExpression) { > PsiReplacementUtil. replaceExpression ((PsiExpression)element, myEnumName + > ".values()" ); > } > Could be replaced with > if (descriptor.getPsiElement() instanceof (PsiNewExpression | > PsiArrayInitializerExpression) expression) { > PsiReplacementUtil. replaceExpression (expression, myEnumName + ".values()" ); > } > Or > Object normalized = value; > if (value instanceof Byte || value instanceof Short) { > normalized = ((Number)value).intValue(); > } > Could be replaced with > if (value instanceof (Byte | Short) number) { > normalized = number.intValue(); > } > And so on. This allows reducing the number of typecasts even further. > With best regards, > Tagir Valeev. > ??, 9 ???. 2020 ?., 21:59 Brian Goetz < [ mailto:brian.goetz at oracle.com | > brian.goetz at oracle.com ] >: >> There's a simple (though unsatisfying) answer to your student's question: >> because catch is special. (But this answer belies an important point: ad-hoc >> syntactic "patches" like multi-catch may be satisfying in the short term, but >> they always beget a flurry of "for consistency" arguments, which can often not >> be consistently satisfied.) >> Essentially, you are asking whether (a) OR patterns make sense, and (b) whether >> we can reuse the syntax A|B for OR'ed type patterns. Let's pull on that string >> for a bit. >> Suppose that `(T|U) tu` were a type pattern. The type test is clear enough, but >> what is the type of `tu`? There are two candidates; a union type and LUB. >> (Union types are clearly more general, but what does multi-catch do? It does >> LUB. See JLS 14.20.) I suspect your preferred answer is "union type", since >> that's a more information-preserving answer. One downside here is it provides >> a(nother) vector for non-denotable weird types to escape to where users can >> observe them. (We faced a similar issue in Lambda (and LVTI): both allowed >> intersection types to escape more freely into the wild, and both had >> consequences.) It's a possibility, but I'm not sure it clears the bar for >> risk/reward. >> A more general answer that doesn't involve introducing new syntactic forms into >> the language would be to dust off the proposal for binding variable merging: >> if (x instanceof RuntimeException e || x instanceof Error e) { X } >> Our original design allowed for this but we backed off because it was unclear >> whether the return-on-complexity was justified. Here, we'd see that we have two >> patterns, each with a binding of the same name, and exactly one of which can >> produce a binding at X. So we can give `e` either a LUB or union type, just as >> with the "shortcut" T|U, but its a more general solution, and doesn't tease >> users with a "fake" union type syntax. >> Note that the "merging" situation has another analogue with the same semantics: >> fallthrough. The above is like: >> switch (x) { >> case RuntimeException e: >> case Error e: >> X; >> } >> If fake union types are not great, what about real union types? Well, it's been >> shown to be possible ( [ >> https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.158.1253&rep=rep1&type=pdf >> | >> https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.158.1253&rep=rep1&type=pdf >> ] ), but its a lot; it seems like the cost-benefit isn't there either. >> Overall, I'm skeptical that the cost/benefit of union type patterns is positive, >> but they are doable. >> On 10/9/2020 6:11 AM, Remi Forax wrote: >>> Following the course on exceptions, where i explain that a catch() is an >>> instanceof, >>> two different students ask me why catch() can use '|' in between the exception >>> types but instanceof can not. >>> i.e why this code works >>> try { >>> ... >>> } catch(RuntimeException | Error e) { >>> throw e; >>> } catch(Throwable t) { >>> ... >>> } >>> but this one doesn't >>> try { >>> ... >>> } catch(Throwable t) { >>> if (t instanceof RuntimeException | Error e) { >>> throw e; >>> } >>> ... >>> } >>> I wonder if people will want to do pattern matching on exceptions ? >>> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Mon Oct 12 19:55:40 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 12 Oct 2020 20:55:40 +0100 Subject: [patterns-instanceof] Spec for next version of Patterns in instanceof JEP In-Reply-To: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> References: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> Message-ID: <4145F2FA-089E-4BC3-B7EE-6BBCFE363379@oracle.com> Following the announcement of JEP 394 [1] the latest version of the spec is now available at: http://cr.openjdk.java.net/~gbierman/jep394/latest/ I have fixed the bugs spotted by Brian in the previous draft. I have also tidied up the treatment of annotations (See sections 9.6.4.1 and 9.7.4 along with the refactored grammar for type test patterns 14.20.1.1) Still time to pass on comments! Gavin [2] https://openjdk.java.net/jeps/394 > On 5 Oct 2020, at 13:53, Gavin Bierman wrote: > > Dear all: > > A draft of the spec for the Patterns in instanceof feature that we plan to > finalize in JDK 16 is now available: > > http://cr.openjdk.java.net/~gbierman/8250623/latest/ > > [NB: The URL will change once we have a JEP number, and will be announced.] > > The changes are the same as those in the second preview that was released in > Java SE 16, except for minor editorial changes and the following: > > - To lift the restriction that pattern variables are implicitly final. This > allows pattern variables to be considered as a strict subset of local > variables. A number of simplifications to the spec result from this change. > > - To make it a compile-time error for an expression of type _S_ to be matched > against a pattern of type _T_, where _S_ is a subtype of _T_. (This pattern > match will always succeed and is then pointless. The opposite case, where a > pattern match will always fail is already a compile-time error.) > > Comments welcome! > > Gavin From brian.goetz at oracle.com Tue Oct 13 17:18:48 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 13 Oct 2020 13:18:48 -0400 Subject: [patterns-instanceof] Spec for next version of Patterns in instanceof JEP In-Reply-To: <4145F2FA-089E-4BC3-B7EE-6BBCFE363379@oracle.com> References: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> <4145F2FA-089E-4BC3-B7EE-6BBCFE363379@oracle.com> Message-ID: This seems to cleverly sidestep the issue of pattern totality (by outlawing the total type pattern on the RHS of instanceof for now) and leave us room to go either way in the future.? So, +1. I think you can drop If the value of the/RelationalExpression/is the null reference, then the result is|false|. because you already say * A value,/V/, that is not the null reference value matches a type test pattern of type/T/if/V/can be cast to/T/without raising a|ClassCastException|, in which case the value/V/is assigned to the pattern variable declared by the type test pattern; and it does not match otherwise. When it comes time to have patterns that do match null, we can expand 14.30.3 at that time; that seems better than backpedaling in 15.20.2. On 10/12/2020 3:55 PM, Gavin Bierman wrote: > Following the announcement of JEP 394 [1] the latest version of the spec is now available at: > > http://cr.openjdk.java.net/~gbierman/jep394/latest/ > > I have fixed the bugs spotted by Brian in the previous draft. I have also tidied up the treatment of annotations (See sections 9.6.4.1 and 9.7.4 along with the refactored grammar for type test patterns 14.20.1.1) > > Still time to pass on comments! > Gavin > > [2] https://openjdk.java.net/jeps/394 > >> On 5 Oct 2020, at 13:53, Gavin Bierman wrote: >> >> Dear all: >> >> A draft of the spec for the Patterns in instanceof feature that we plan to >> finalize in JDK 16 is now available: >> >> http://cr.openjdk.java.net/~gbierman/8250623/latest/ >> >> [NB: The URL will change once we have a JEP number, and will be announced.] >> >> The changes are the same as those in the second preview that was released in >> Java SE 16, except for minor editorial changes and the following: >> >> - To lift the restriction that pattern variables are implicitly final. This >> allows pattern variables to be considered as a strict subset of local >> variables. A number of simplifications to the spec result from this change. >> >> - To make it a compile-time error for an expression of type _S_ to be matched >> against a pattern of type _T_, where _S_ is a subtype of _T_. (This pattern >> match will always succeed and is then pointless. The opposite case, where a >> pattern match will always fail is already a compile-time error.) >> >> Comments welcome! >> >> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Tue Oct 13 19:55:34 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 13 Oct 2020 13:55:34 -0600 Subject: [sealed] JDK-8243582: JVMS change to class loading error Message-ID: <700D7804-46EC-4D96-8A32-B3279FC32AEE@oracle.com> In anticipation of sealed classes, JDK-8243582 describes a JVMS change that modifies an error thrown in class loading as of Java SE 16. https://bugs.openjdk.java.net/browse/JDK-8243582 Specifically, VerifyErrors that were thrown during class loading due to `final` constraint violations will become IncompatibleClassChangeErrors. (And similarly, under `--enable-preview`, for `sealed` constraint violations.) We feel like it's an appropriate change to make (class loading is not part of verification, and the handling of this check during verification in JVMS is just wrong), but want to make sure there aren't any concerns from a broader audience. From forax at univ-mlv.fr Wed Oct 14 12:53:07 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 14 Oct 2020 14:53:07 +0200 (CEST) Subject: [patterns-instanceof] Spec for next version of Patterns in instanceof JEP In-Reply-To: References: <8D3D7432-E692-4A3D-9C29-06CE78EAC579@oracle.com> <4145F2FA-089E-4BC3-B7EE-6BBCFE363379@oracle.com> Message-ID: <665566333.903376.1602679987824.JavaMail.zimbra@u-pem.fr> > De: "Brian Goetz" > ?: "Gavin Bierman" , "amber-spec-experts" > > Envoy?: Mardi 13 Octobre 2020 19:18:48 > Objet: Re: [patterns-instanceof] Spec for next version of Patterns in instanceof > JEP > This seems to cleverly sidestep the issue of pattern totality (by outlawing the > total type pattern on the RHS of instanceof for now) and leave us room to go > either way in the future. So, +1. oh, no, all my codes that does a "instanceof Object o" does not work anymore :) so good to me too. R?mi [...] > On 10/12/2020 3:55 PM, Gavin Bierman wrote: >> Following the announcement of JEP 394 [1] the latest version of the spec is now >> available at: [ http://cr.openjdk.java.net/~gbierman/jep394/latest/ | >> http://cr.openjdk.java.net/~gbierman/jep394/latest/ ] I have fixed the bugs >> spotted by Brian in the previous draft. I have also tidied up the treatment of >> annotations (See sections 9.6.4.1 and 9.7.4 along with the refactored grammar >> for type test patterns 14.20.1.1) >> Still time to pass on comments! >> Gavin >> [2] [ https://openjdk.java.net/jeps/394 | https://openjdk.java.net/jeps/394 ] >>> On 5 Oct 2020, at 13:53, Gavin Bierman [ mailto:gavin.bierman at oracle.com | >>> ] wrote: >>> Dear all: >>> A draft of the spec for the Patterns in instanceof feature that we plan to >>> finalize in JDK 16 is now available: [ >>> http://cr.openjdk.java.net/~gbierman/8250623/latest/ | >>> http://cr.openjdk.java.net/~gbierman/8250623/latest/ ] [NB: The URL will change >>> once we have a JEP number, and will be announced.] >>> The changes are the same as those in the second preview that was released in >>> Java SE 16, except for minor editorial changes and the following: >>> - To lift the restriction that pattern variables are implicitly final. This >>> allows pattern variables to be considered as a strict subset of local >>> variables. A number of simplifications to the spec result from this change. >>> - To make it a compile-time error for an expression of type _S_ to be matched >>> against a pattern of type _T_, where _S_ is a subtype of _T_. (This pattern >>> match will always succeed and is then pointless. The opposite case, where a >>> pattern match will always fail is already a compile-time error.) >>> Comments welcome! >>> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Wed Oct 14 15:31:54 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Wed, 14 Oct 2020 16:31:54 +0100 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: Message-ID: <792B26D7-9DB1-4EAB-8FF3-D8AFC5637E8D@oracle.com> > On 29 Sep 2020, at 16:50, Tagir Valeev wrote: > > The only nit-pick is that it says "if a canonical constructor > is declared explicitly". However, from 8.10.4 one may derive that > compact constructor is a canonical constructor, so if compact > canonical constructor is explicitly declared then, per 8.10.5 it looks > like annotation should be discarded but it's not. Probably it's better > to write in 8.10.5 something like "if a non-compact canonical > constructor is declared explicitly". Well, probably I'm seeing things. No, you?re right. A compact constructor gets its formal parameter list from the record component list, and so you get any annotations that are applicable. I have clarified this in 8.10.4. If we keep 8.10.5, then it needs wording much as you suggest; but I see that Brian is arguing that we shouldn?t treat non-propagating annotations as errors. Gavin From gavin.bierman at oracle.com Wed Oct 14 21:19:16 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Wed, 14 Oct 2020 22:19:16 +0100 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: <4349115a-df81-8c90-5d93-a36dd7939ff3@oracle.com> Message-ID: Hi Tagir, Upon reflection I think it?s best that we don?t treat this as a compile-time error, but just as the specified behaviour. The warning vs error thing is a grey area, especially in Java, but having bounced this around it seems like it has fallen (just) in the warning side of the divide. As you say, this is a great opportunity for IDEs to be helpful :-) Just to be clear: @Target(ElementType.METHOD) @interface A { } record R1(@A int x) { // Where did the annotation go? int x() { return x; } } In this example, what I am proposing is that there is no compile-time error and the A annotation will simply not be propagated anywhere. [I am expecting that most developers in this case would have added the RECORD_COMPONENT target, so the annotation does not disappear completely.] Thanks, Gavin > On 5 Oct 2020, at 05:47, Tagir Valeev wrote: > > On Sat, Oct 3, 2020 at 12:09 AM Brian Goetz wrote: >> >> I think we're reaching diminishing returns here. > > Well, to me, it's not diminishing. It's certainly a piece of code that > will not have any effect after compilation, so it's a mistake. > Reporting it is not that hard. Having a class/runtime annotation in > source code that disappears completely in the compiled class-file > without any warning sounds confusing and may cause subtle problems. > Well, of course, we can create an inspection in IntelliJ IDEA to warn > about these cases, and people will like IntelliJ IDEA even more, but I > believe it's a compiler job to reject this, like Java already does > with e.g. unreachable code. > > With best regards, > Tagir Valeev. -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Wed Oct 14 21:19:22 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Wed, 14 Oct 2020 22:19:22 +0100 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: <2238a301-1a62-bf23-73d7-be1f29f9ee84@oracle.com> References: <2238a301-1a62-bf23-73d7-be1f29f9ee84@oracle.com> Message-ID: > On 29 Sep 2020, at 22:32, Brian Goetz wrote: > > Should @SafeVarargs be allowed to be applied to a varargs record? Should it get propagated to the constructor in that case? Or do we just have people declare a compact constructor with @SafeVarargs? What is the correct use of @SV for varargs records? Right now, if you annotate a record component with @SafeVarArgs you are in trouble, because @SafeVarArgs is applicable in the method context, and so it will be propagated to the accessor method. As the accessor method is a fixed arity method, that?s going to be an error. We say currently that you should declare a constructor with @SafeVarArgs. That seems reasonable to me. Unless someone thinks otherwise, I don?t think we should be special-casing this annotation. (I guess we can always do this in a later release.) Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Oct 15 20:42:03 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Thu, 15 Oct 2020 22:42:03 +0200 (CEST) Subject: [records] Spec for next version of Record Classes JEP Message-ID: <529974468.715696.1602794523487.JavaMail.zimbra@u-pem.fr> > De: "Gavin Bierman" > ?: "Tagir Valeev" > Cc: "Brian Goetz" , "amber-spec-experts" > > Envoy?: Mercredi 14 Octobre 2020 23:19:16 > Objet: Re: [records] Spec for next version of Record Classes JEP > Hi Tagir, > Upon reflection I think it?s best that we don?t treat this as a compile-time > error, but just as the specified behaviour. The warning vs error thing is a > grey area, especially in Java, but having bounced this around it seems like it > has fallen (just) in the warning side of the divide. As you say, this is a > great opportunity for IDEs to be helpful :-) Maybe i'm the only old guy here but Java was once touted as the best programming language because it has no warning. Warnings were introduced later in Java 5, because of generics. > Just to be clear: > @Target(ElementType.METHOD) > @interface A { } > record R1(@A int x) { // Where did the annotation go? > int x() { return x; } > } > In this example, what I am proposing is that there is no compile-time error and > the A annotation will simply not be propagated anywhere. I think it should be an error because from a user POV it's like if the compiler silently remove the annotation behind your back. As an analogy, when you put an annotation in a target not listed in @Target(), the compiler emit an error, it doesn't silently remove the annotation. > [I am expecting that most developers in this case would have added the > RECORD_COMPONENT target, so the annotation does not disappear completely.] A lot of annotations already exist. And people writing annotations (lib developers) are really few compared to people writing records (app developers). > Thanks, > Gavin regards, R?mi >> On 5 Oct 2020, at 05:47, Tagir Valeev < [ mailto:amaembo at gmail.com | >> amaembo at gmail.com ] > wrote: >> On Sat, Oct 3, 2020 at 12:09 AM Brian Goetz < [ mailto:brian.goetz at oracle.com | >> brian.goetz at oracle.com ] > wrote: >>> I think we're reaching diminishing returns here. >> Well, to me, it's not diminishing. It's certainly a piece of code that >> will not have any effect after compilation, so it's a mistake. >> Reporting it is not that hard. Having a class/runtime annotation in >> source code that disappears completely in the compiled class-file >> without any warning sounds confusing and may cause subtle problems. >> Well, of course, we can create an inspection in IntelliJ IDEA to warn >> about these cases, and people will like IntelliJ IDEA even more, but I >> believe it's a compiler job to reject this, like Java already does >> with e.g. unreachable code. >> With best regards, >> Tagir Valeev. From gavin.bierman at oracle.com Mon Oct 19 16:34:47 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 19 Oct 2020 17:34:47 +0100 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: Message-ID: Following the announcement of JEP 395 [1] the latest version of the spec is now available at: http://cr.openjdk.java.net/~gbierman/jep395/latest/ This covers the various bugs in an earlier draft that have been reported on this list. As per my earlier email this spec does not treat a non-propagating annotation in the case where an explicit accessor method has been declared as a compile-time error. I still think this is a better design, but if anyone feels strongly, please email the list! Thanks, Gavin [1] https://openjdk.java.net/jeps/395 From amaembo at gmail.com Tue Oct 20 03:08:53 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 20 Oct 2020 10:08:53 +0700 Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: Message-ID: Hello! I still feel that there should be a compilation error if record component annotation is ineffective due to explicit specification of accessor method and/or non-compact canonical constructor and completely support Remi's argument. > [I am expecting that most developers in this case would have added the RECORD_COMPONENT target, so the annotation does not disappear completely.] I should also note that it's hard to add a RECORD_COMPONENT target to the library annotation if it's intended to be used with earlier Java versions (don't tell me about multi-release jars!) And the semantics of RECORD_COMPONENT target is not very clear, as clients work directly with constructors and accessors (call them), but they don't work with record components. For most of the clients, the record is not very different from the ordinary class, and I'm not sure that all the clients should check if the called method is a record accessor, find the corresponding component and check its annotations, in addition to checking the method annotations in the first place. Also, I expect that people will sometimes convert records to ordinary classes (e.g. suppose that at some point it becomes desired to have a private mutable field to cache something). In this case, they can mechanically translate the record to the class (IntelliJ already provides this facility) but the annotation will disappear completely. Well, if all of this is not convincing, let's stop this discussion. I filed an enhancement request for IntelliJ to report the warning in this case: https://youtrack.jetbrains.com/issue/IDEA-253317 For our users, this will be enough (after all, anyone may set a severity level to 'ERROR'). Other IDEs may also do the same. With best regards, Tagir Valeev. On Mon, Oct 19, 2020 at 11:35 PM Gavin Bierman wrote: > > Following the announcement of JEP 395 [1] the latest version of the spec is now available at: > > http://cr.openjdk.java.net/~gbierman/jep395/latest/ > > This covers the various bugs in an earlier draft that have been reported on this list. As per my earlier email this spec does not treat a non-propagating annotation in the case where an explicit accessor method has been declared as a compile-time error. I still think this is a better design, but if anyone feels strongly, please email the list! > > Thanks, > Gavin > > [1] https://openjdk.java.net/jeps/395 From forax at univ-mlv.fr Wed Oct 21 14:22:27 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 21 Oct 2020 16:22:27 +0200 (CEST) Subject: [records] Spec for next version of Record Classes JEP In-Reply-To: References: Message-ID: <467771801.1695736.1603290147809.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Tagir Valeev" > ?: "Gavin Bierman" > Cc: "amber-spec-experts" > Envoy?: Mardi 20 Octobre 2020 05:08:53 > Objet: Re: [records] Spec for next version of Record Classes JEP > Hello! > > I still feel that there should be a compilation error if record > component annotation is ineffective due to explicit specification of > accessor method and/or non-compact canonical constructor and > completely support Remi's argument. > >> [I am expecting that most developers in this case would have added the >> RECORD_COMPONENT target, so the annotation does not disappear completely.] > > I should also note that it's hard to add a RECORD_COMPONENT target to > the library annotation if it's intended to be used with earlier Java > versions (don't tell me about multi-release jars!) And the semantics > of RECORD_COMPONENT target is not very clear, as clients work directly > with constructors and accessors (call them), but they don't work with > record components. For most of the clients, the record is not very > different from the ordinary class, and I'm not sure that all the > clients should check if the called method is a record accessor, find > the corresponding component and check its annotations, in addition to > checking the method annotations in the first place. Also, I expect > that people will sometimes convert records to ordinary classes (e.g. > suppose that at some point it becomes desired to have a private > mutable field to cache something). In this case, they can mechanically > translate the record to the class (IntelliJ already provides this > facility) but the annotation will disappear completely. Obviously, I agree with Tagir. Conceptually, the annotation propagation is a dangerous idea but we hope a lesser evil. We fundamentally don't need annotation propagation, it's a nice to have to accelerate adoption of records, libraries that read annotations and annotations itself may not need to be updated to use records because we propagate annotations. But it comes with a price, the annotation propagation mechanism is implicit, so most people will not be aware of that mechanism, after all they put the annotation on the record components not on something else. Given it's an implicit algorithm our users may be not aware, it should not fail silently. If people are able to declare an annotation on a record component, either it works or it does not. But having a weird third state, it's useless, is bad design in my opinion. > > Well, if all of this is not convincing, let's stop this discussion. I > filed an enhancement request for IntelliJ to report the warning in > this case: > https://youtrack.jetbrains.com/issue/IDEA-253317 > For our users, this will be enough (after all, anyone may set a > severity level to 'ERROR'). Other IDEs may also do the same. IDEs can help but if JetBrain goes bankrupt one day, i hope we will still able to use Java. > > With best regards, > Tagir Valeev. R?mi > > On Mon, Oct 19, 2020 at 11:35 PM Gavin Bierman wrote: >> >> Following the announcement of JEP 395 [1] the latest version of the spec is now >> available at: >> >> http://cr.openjdk.java.net/~gbierman/jep395/latest/ >> >> This covers the various bugs in an earlier draft that have been reported on this >> list. As per my earlier email this spec does not treat a non-propagating >> annotation in the case where an explicit accessor method has been declared as a >> compile-time error. I still think this is a better design, but if anyone feels >> strongly, please email the list! >> >> Thanks, >> Gavin >> > > [1] https://openjdk.java.net/jeps/395 From gavin.bierman at oracle.com Fri Oct 23 15:16:20 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 23 Oct 2020 16:16:20 +0100 Subject: [sealed-classes] Spec for next version of Sealed Classes Message-ID: Dear all: Drafts of the specs for the Sealed Classes feature that we plan to preview for a second time in JDK 16 are now available: http://cr.openjdk.java.net/~gbierman/8246775/latest/ [NB: The URL will change once we have a JEP number, and will be announced.] The changes are the same as those in the first preview that was released in Java SE 15, except for minor editorial changes and the following: - Clarification the use of context when applying the lexical grammar, particularly in the identification of contextual keywords (formerly described as "restricted identifiers" and "restricted keywords"). This is detailed in a companion document entitled ?Contextual Keywords". The keywords `sealed`, `non-sealed`, and `permits` are now defined as new instances of contextual keywords (3.9). - This spec now assumes that the changes detailed in companion documents entitled "Consistent Class and Interface Terminology? and "Local and Nested Static Declarations" have been applied (these are being introduced as part of the Records JEP). In particular, this means that Java SE 16 will support static declarations in two new positions: 1. Local, implicitly-static interfaces and enum classes 2. Static members of inner classes This requires asserting that local interfaces are not permitted to be `sealed`. (14.3) - To enhance narrowing reference conversion to allow for stricter checking of cast conversions with respect to sealed type hierarchies (5.1.6.1). - Local classes are not considered when determining implicitly declared permitted direct subclasses of a `sealed` class or `sealed` interface (8.1.6, 9.1.4). Comments welcome! Gavin From gavin.bierman at oracle.com Fri Oct 23 15:27:17 2020 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 23 Oct 2020 16:27:17 +0100 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> Message-ID: <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> Just to follow this up; we have decided to change the signature of permittedSubclasses to the following: public Class[] permittedSubclasses() {} Thanks! Gavin > On 8 May 2020, at 23:53, Remi Forax wrote: > > The current draft of the reflection API for the sealed keyword adds a method getPermittedSubclasses() [1] to java.lang.Class. > > I'm not fully sure that returning an array of ClassDesc is the right choice here, mainly for two reasons, > > 1/ it's weird to return an array of ClassDesc when all others similar methods return an array of Class, > I know why a ClassDesc might be "better" because it avoid the class loading, > but it also means that now to fully understand java.lang.Class, people has to understand how java.lang.constant works. > The java.lang.constant API was not designed for that, the first line of the overview of this package talks about descriptors, constant pool and indy, not something beginners should worry about. > > 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error prone from a user POV, to resolve a ClassDesc to a class, the user as to provide a Lookup > and there is a good chance that users will pick the wrong ones. The number of people that understand classloading and how Lookup works is < 10, > even experts struggle given the number of time the Lookup API as to be patched in recent years. Returning a ClassDesc in this context is like asking a child > to read the serial number of a loaded gun. > Perhaps a way to mitigate that is to provide the code a user should use to get the equivalent classes in the javadoc of getPermittedSubclasses(). > > cheers, > R?mi > > [1] https://bugs.openjdk.java.net/browse/JDK-8244556 From brian.goetz at oracle.com Fri Oct 23 15:36:44 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 23 Oct 2020 11:36:44 -0400 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> Message-ID: <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> FTR: this was largely a "for consistency" decision, because nestmates does it the same way.? (Which is to say, it was a deliberate suboptimal choice aimed at minimizing the number of API idioms that users of reflection had to deal with.) On 10/23/2020 11:27 AM, Gavin Bierman wrote: > Just to follow this up; we have decided to change the signature of permittedSubclasses to the following: > > public Class[] permittedSubclasses() {} > > Thanks! > Gavin > >> On 8 May 2020, at 23:53, Remi Forax wrote: >> >> The current draft of the reflection API for the sealed keyword adds a method getPermittedSubclasses() [1] to java.lang.Class. >> >> I'm not fully sure that returning an array of ClassDesc is the right choice here, mainly for two reasons, >> >> 1/ it's weird to return an array of ClassDesc when all others similar methods return an array of Class, >> I know why a ClassDesc might be "better" because it avoid the class loading, >> but it also means that now to fully understand java.lang.Class, people has to understand how java.lang.constant works. >> The java.lang.constant API was not designed for that, the first line of the overview of this package talks about descriptors, constant pool and indy, not something beginners should worry about. >> >> 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error prone from a user POV, to resolve a ClassDesc to a class, the user as to provide a Lookup >> and there is a good chance that users will pick the wrong ones. The number of people that understand classloading and how Lookup works is < 10, >> even experts struggle given the number of time the Lookup API as to be patched in recent years. Returning a ClassDesc in this context is like asking a child >> to read the serial number of a loaded gun. >> Perhaps a way to mitigate that is to provide the code a user should use to get the equivalent classes in the javadoc of getPermittedSubclasses(). >> >> cheers, >> R?mi >> >> [1] https://bugs.openjdk.java.net/browse/JDK-8244556 -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Fri Oct 23 17:17:08 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 23 Oct 2020 19:17:08 +0200 (CEST) Subject: [sealed-classes] Spec for next version of Sealed Classes In-Reply-To: References: Message-ID: <963658049.1932974.1603473428924.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Gavin Bierman" > ?: "amber-spec-experts" > Envoy?: Vendredi 23 Octobre 2020 17:16:20 > Objet: [sealed-classes] Spec for next version of Sealed Classes > Dear all: > Hi Gavin, > Drafts of the specs for the Sealed Classes feature that we plan to preview for > a second time in JDK 16 are now available: > > http://cr.openjdk.java.net/~gbierman/8246775/latest/ > > [NB: The URL will change once we have a JEP number, and will be announced.] > > The changes are the same as those in the first preview that was released in Java > SE 15, except for minor editorial changes and the following: > > - Clarification the use of context when applying the lexical grammar, > particularly in the identification of contextual keywords (formerly described > as "restricted identifiers" and "restricted keywords"). This is detailed in a > companion document entitled ?Contextual Keywords". The keywords `sealed`, > `non-sealed`, and `permits` are now defined as new instances of contextual > keywords (3.9). > > - This spec now assumes that the changes detailed in companion documents > entitled "Consistent Class and Interface Terminology? and "Local and Nested > Static Declarations" have been applied (these are being introduced as part of > the Records JEP). In particular, this means that Java SE 16 will support > static declarations in two new positions: > > 1. Local, implicitly-static interfaces and enum classes > 2. Static members of inner classes So now the lazy class init idiom the can be written like this public class DB { public static DB getInstance() { class Holder { private static final DB INSTANCE = new DB(); } return Holder.INSTANCE; } } Question, does the compiler add a hidden field inside Holder that reference DB ? If yes, then declaring the class Holder static should remove that field like with with a record public class DB { public static DB getInstance() { record Holder() { private static final DB INSTANCE = new DB(); } return Holder.INSTANCE; } } > > This requires asserting that local interfaces are not permitted to be > `sealed`. (14.3) > > - To enhance narrowing reference conversion to allow for stricter checking of > cast conversions with respect to sealed type hierarchies (5.1.6.1). the discussion is clear, i'm wondering if you are using the term "named" class because anonymous classes are not declared final (as a weird twist of the history). > > - Local classes are not considered when determining implicitly declared > permitted direct subclasses of a `sealed` class or `sealed` interface > (8.1.6, 9.1.4). > > > Comments welcome! > > Gavin R?mi From brian.goetz at oracle.com Fri Oct 23 18:23:14 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 23 Oct 2020 14:23:14 -0400 Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: No one bit on this, but let me just point out a connection that may help motivate this: sealed types are union types. If we say ??? sealed interface A permits X, Y { } then this is like: ??? A = X | Y A structural interpretation of union types says that ??? X <: I? &? Y <: I ? --> ?? A <: I Essentially, this idea says we can borrow from the union nature of sealed types when convenient, to provide better type checking. As mentioned below, the real value of this is not avoiding the cast, but letting the type system do more of the work, so that if the implicit assumption is later invalidated, the compiler can catch save us from ourselves.? A cast would push assumption failures to runtime, where they are harder to detect and potentially more costly. On 10/9/2020 11:16 AM, Brian Goetz wrote: > Here's an idea that I've been thinking about for a few days, it's not > urgent to decide on now, but I think it is worth considering in the > background. > > When we did expression switch, we had an interesting discussion about > what is the point of not writing a default clause on an optimistically > total enum switch (and the same reasoning applies on switches on > sealed types.)? Suppose I have: > > ??? var x = switch (trafficLight) { > ??????? case RED -> ... > ??????? case YELLOW -> ... > ??????? case GREEN -> ... > ??? } > > People like this because they don't have to write a silly default > clause that just throws an silly exception with a silly message (and > as a bonus, is hard to cover with tests.) But Kevin pointed out that > this is really the lesser benefit of the compiler reasoning about > exhaustiveness; the greater benefit is that it allows you to more > precisely capture assumptions in your program about totality, which > the compiler can validate for you.? If later, someone adds BLUE to > traffic lights, the above switch fails to recompile, and we are > constructively informed about an assumption being violated, whereas if > we had a default clause, the fact that our assumption went stale gets > swept under the rug. > > I was writing some code with sealed classes the other day, and I > discovered an analogue of this which we may want to consider.? I had: > > ??? public sealed interface Foo > ??????? permits MyFooImpl { } > ??? private class MyFooImpl implements Foo { } > > which I think we can agree will be a common enough pattern. And I > found myself wanting to write: > > ??? void m(Foo f) { > ??????? MyFooImpl mfi = (MyFooImpl) f; > ??????? ... > ??? } > > This line of code is based on the assumption that Foo is sealed to > permit only MyFooImpl, which is a valid assumption right now, since > all this code exists only on my workstation. But some day, someone > else may extend Foo to permit two private implementations, but may not > be aware of the time bombs I've buried here. > > Suppose, though, that U were assignable to T if U is a sealed type and > all permitted subtypes of U are assignable to T. Then I'd be able to > write: > > ??? MyFooImpl mfi = f; > > Not only do I not have to write the cast (the minor benefit), but > rather than burying the assumption "all implementations of Foo are > castable to MyFooImpl" in implementation code that can only fail at > runtime, I can capture it in a way the compiler can verify on every > recompilation, and when the underlying assumption is invalidated, so > is the code that makes the assumption.? This seems less brittle (the > major benefit.) > > This generalizes, of course.? Suppose we have: > > ??? sealed interface X permits A, B { } > ??? class A extends Base implements X { } > ??? class B extends Base implements X { } > > Then X becomes assignable to Base. > > I'm not quite sure yet how to feel about this, but I really do like > the idea of being able to put the assumptions like "X must be a Y" -- > which people _will_ make -- in a place where the compiler can > typecheck it. > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amalloy at google.com Fri Oct 23 18:35:57 2020 From: amalloy at google.com (Alan Malloy) Date: Fri, 23 Oct 2020 11:35:57 -0700 Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: I missed your earlier message, and I have one question that probably I would already know the answer to if I were keeping up with the sealed type specs. Is the permits clause of a sealed type public information, or an implementation detail? That is, imagine public sealed interface Foo permits A {} public class A implements Foo {} How does a client from outside of the package perceive this? Of course they know Foo is sealed and they cannot implement it. I assume they also know about A, since if they wanted to they could look at A and see it implements Foo. But do they know whether there is some secret B implementation also? Do they know whether there are 0, 1, or more non-public implementations of Foo? It seems like clients would need to know that in order for this idea to work. If you want it only to work in the same compilation unit as Foo, that seems simpler but may annoy clients, who can "see" that there is only one implementation, so why shouldn't they be able to auto-cast it. On Fri, Oct 23, 2020 at 11:25 AM Brian Goetz wrote: > No one bit on this, but let me just point out a connection that may help > motivate this: sealed types are union types. > > If we say > > sealed interface A permits X, Y { } > > then this is like: > > A = X | Y > > A structural interpretation of union types says that > > X <: I & Y <: I --> A <: I > > Essentially, this idea says we can borrow from the union nature of sealed > types when convenient, to provide better type checking. > > As mentioned below, the real value of this is not avoiding the cast, but > letting the type system do more of the work, so that if the implicit > assumption is later invalidated, the compiler can catch save us from > ourselves. A cast would push assumption failures to runtime, where they > are harder to detect and potentially more costly. > > > On 10/9/2020 11:16 AM, Brian Goetz wrote: > > Here's an idea that I've been thinking about for a few days, it's not > urgent to decide on now, but I think it is worth considering in the > background. > > When we did expression switch, we had an interesting discussion about what > is the point of not writing a default clause on an optimistically total > enum switch (and the same reasoning applies on switches on sealed types.) > Suppose I have: > > var x = switch (trafficLight) { > case RED -> ... > case YELLOW -> ... > case GREEN -> ... > } > > People like this because they don't have to write a silly default clause > that just throws an silly exception with a silly message (and as a bonus, > is hard to cover with tests.) But Kevin pointed out that this is really > the lesser benefit of the compiler reasoning about exhaustiveness; the > greater benefit is that it allows you to more precisely capture assumptions > in your program about totality, which the compiler can validate for you. > If later, someone adds BLUE to traffic lights, the above switch fails to > recompile, and we are constructively informed about an assumption being > violated, whereas if we had a default clause, the fact that our assumption > went stale gets swept under the rug. > > I was writing some code with sealed classes the other day, and I > discovered an analogue of this which we may want to consider. I had: > > public sealed interface Foo > permits MyFooImpl { } > private class MyFooImpl implements Foo { } > > which I think we can agree will be a common enough pattern. And I found > myself wanting to write: > > void m(Foo f) { > MyFooImpl mfi = (MyFooImpl) f; > ... > } > > This line of code is based on the assumption that Foo is sealed to permit > only MyFooImpl, which is a valid assumption right now, since all this code > exists only on my workstation. But some day, someone else may extend Foo > to permit two private implementations, but may not be aware of the time > bombs I've buried here. > > Suppose, though, that U were assignable to T if U is a sealed type and all > permitted subtypes of U are assignable to T. Then I'd be able to write: > > MyFooImpl mfi = f; > > Not only do I not have to write the cast (the minor benefit), but rather > than burying the assumption "all implementations of Foo are castable to > MyFooImpl" in implementation code that can only fail at runtime, I can > capture it in a way the compiler can verify on every recompilation, and > when the underlying assumption is invalidated, so is the code that makes > the assumption. This seems less brittle (the major benefit.) > > This generalizes, of course. Suppose we have: > > sealed interface X permits A, B { } > class A extends Base implements X { } > class B extends Base implements X { } > > Then X becomes assignable to Base. > > I'm not quite sure yet how to feel about this, but I really do like the > idea of being able to put the assumptions like "X must be a Y" -- which > people _will_ make -- in a place where the compiler can typecheck it. > > > > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Oct 23 18:47:09 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 23 Oct 2020 14:47:09 -0400 Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: A sealed type and its permitted subtypes may have different access modifiers, by design.? For example, we expect cases like the following, where the implementations are encapsulated, to be common: ??? public sealed interface Foo permits FooImpl { } ??? private class FooImpl implements Foo { } The permits list of a sealed type X is visible to anyone to whom X is visible; it's just that they may not be able to access those classes.? (You can say Class.forName("X") and get an answer even when X is not accessible, you just won't be able to operate on it.? Similarly, you can refer to an inacessible class in source code, and you'll get an error like "class not accessible", rather than "class not found.") If we have: ??? public sealed interface Foo permits A, B { } ??? public class A implements Foo { } ??? private class B implements Foo { } and an arms-length client did a switch over Foo: ??? var x = switch (foo) { ??????? case A: ... ??? } the compiler would know it is not exhaustive, because it knows there are additional subtypes.? Within the package of Foo, it could switch over them all: ??? var x = switch (foo) { ??????? case A: ... ??????? case B: ... ??? } and the compiler will see it is exhaustive. So, sealing is largely independent of accessibility.? If everyone is inside the circle, you get the type extra checking that sealing provides; to someone outside the circle, they can see what they can see, and know there is stuff they don't see. But, these "relaxed conversions" apply between two types, and you have to be able to access both types in order to write the assignment anyway.? So I think it is not an issue? On 10/23/2020 2:35 PM, Alan Malloy wrote: > I missed your earlier message, and I have one question that probably I > would already know the answer to if I were keeping up with the sealed > type specs. Is the permits clause of a sealed type public information, > or an implementation detail? That is, imagine > > public sealed interface Foo permits A {} > > public class A? implements Foo {} > > How does a client from outside of the package perceive this? Of course > they know Foo is sealed and they cannot implement it. I assume they > also know about A, since if they wanted to they could look at A and > see it implements Foo. But do they know whether there is some secret B > implementation also? Do they know whether there are 0, 1, or more > non-public implementations of Foo? It seems like clients would need to > know that in order for this idea to work. If you want it only to work > in the same compilation unit as Foo, that seems simpler but may annoy > clients, who can "see" that there is only one implementation, so why > shouldn't they be able to auto-cast it. > > On Fri, Oct 23, 2020 at 11:25 AM Brian Goetz > wrote: > > No one bit on this, but let me just point out a connection that > may help motivate this: sealed types are union types. > > If we say > > ??? sealed interface A permits X, Y { } > > then this is like: > > ??? A = X | Y > > A structural interpretation of union types says that > > ??? X <: I? &? Y <: I ? --> ?? A <: I > > Essentially, this idea says we can borrow from the union nature of > sealed types when convenient, to provide better type checking. > > As mentioned below, the real value of this is not avoiding the > cast, but letting the type system do more of the work, so that if > the implicit assumption is later invalidated, the compiler can > catch save us from ourselves.? A cast would push assumption > failures to runtime, where they are harder to detect and > potentially more costly. > > > On 10/9/2020 11:16 AM, Brian Goetz wrote: >> Here's an idea that I've been thinking about for a few days, it's >> not urgent to decide on now, but I think it is worth considering >> in the background. >> >> When we did expression switch, we had an interesting discussion >> about what is the point of not writing a default clause on an >> optimistically total enum switch (and the same reasoning applies >> on switches on sealed types.)? Suppose I have: >> >> ??? var x = switch (trafficLight) { >> ??????? case RED -> ... >> ??????? case YELLOW -> ... >> ??????? case GREEN -> ... >> ??? } >> >> People like this because they don't have to write a silly default >> clause that just throws an silly exception with a silly message >> (and as a bonus, is hard to cover with tests.)? But Kevin pointed >> out that this is really the lesser benefit of the compiler >> reasoning about exhaustiveness; the greater benefit is that it >> allows you to more precisely capture assumptions in your program >> about totality, which the compiler can validate for you.? If >> later, someone adds BLUE to traffic lights, the above switch >> fails to recompile, and we are constructively informed about an >> assumption being violated, whereas if we had a default clause, >> the fact that our assumption went stale gets swept under the rug. >> >> I was writing some code with sealed classes the other day, and I >> discovered an analogue of this which we may want to consider.? I had: >> >> ??? public sealed interface Foo >> ??????? permits MyFooImpl { } >> ??? private class MyFooImpl implements Foo { } >> >> which I think we can agree will be a common enough pattern.? And >> I found myself wanting to write: >> >> ??? void m(Foo f) { >> ??????? MyFooImpl mfi = (MyFooImpl) f; >> ??????? ... >> ??? } >> >> This line of code is based on the assumption that Foo is sealed >> to permit only MyFooImpl, which is a valid assumption right now, >> since all this code exists only on my workstation.? But some day, >> someone else may extend Foo to permit two private >> implementations, but may not be aware of the time bombs I've >> buried here. >> >> Suppose, though, that U were assignable to T if U is a sealed >> type and all permitted subtypes of U are assignable to T.? Then >> I'd be able to write: >> >> ??? MyFooImpl mfi = f; >> >> Not only do I not have to write the cast (the minor benefit), but >> rather than burying the assumption "all implementations of Foo >> are castable to MyFooImpl" in implementation code that can only >> fail at runtime, I can capture it in a way the compiler can >> verify on every recompilation, and when the underlying assumption >> is invalidated, so is the code that makes the assumption.? This >> seems less brittle (the major benefit.) >> >> This generalizes, of course.? Suppose we have: >> >> ??? sealed interface X permits A, B { } >> ??? class A extends Base implements X { } >> ??? class B extends Base implements X { } >> >> Then X becomes assignable to Base. >> >> I'm not quite sure yet how to feel about this, but I really do >> like the idea of being able to put the assumptions like "X must >> be a Y" -- which people _will_ make -- in a place where the >> compiler can typecheck it. >> >> >> >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Oct 24 11:35:26 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sat, 24 Oct 2020 13:35:26 +0200 (CEST) Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> Message-ID: <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> Ok nice, I suppose permittedSubclasses has been renamed to getPermittedSubclasses at the same time. R?mi > De: "Brian Goetz" > ?: "Gavin Bierman" , "Remi Forax" > Cc: "amber-spec-experts" , "joe darcy" > > Envoy?: Vendredi 23 Octobre 2020 17:36:44 > Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of > ClassDesc > FTR: this was largely a "for consistency" decision, because nestmates does it > the same way. (Which is to say, it was a deliberate suboptimal choice aimed at > minimizing the number of API idioms that users of reflection had to deal with.) > On 10/23/2020 11:27 AM, Gavin Bierman wrote: >> Just to follow this up; we have decided to change the signature of >> permittedSubclasses to the following: >> public Class[] permittedSubclasses() {} >> Thanks! >> Gavin >>> On 8 May 2020, at 23:53, Remi Forax [ mailto:forax at univ-mlv.fr | >>> ] wrote: >>> The current draft of the reflection API for the sealed keyword adds a method >>> getPermittedSubclasses() [1] to java.lang.Class. >>> I'm not fully sure that returning an array of ClassDesc is the right choice >>> here, mainly for two reasons, >>> 1/ it's weird to return an array of ClassDesc when all others similar methods >>> return an array of Class, >>> I know why a ClassDesc might be "better" because it avoid the class loading, >>> but it also means that now to fully understand java.lang.Class, people has to >>> understand how java.lang.constant works. >>> The java.lang.constant API was not designed for that, the first line of the >>> overview of this package talks about descriptors, constant pool and indy, not >>> something beginners should worry about. >>> 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error >>> prone from a user POV, to resolve a ClassDesc to a class, the user as to >>> provide a Lookup >>> and there is a good chance that users will pick the wrong ones. The number of >>> people that understand classloading and how Lookup works is < 10, >>> even experts struggle given the number of time the Lookup API as to be patched >>> in recent years. Returning a ClassDesc in this context is like asking a child >>> to read the serial number of a loaded gun. >>> Perhaps a way to mitigate that is to provide the code a user should use to get >>> the equivalent classes in the javadoc of getPermittedSubclasses(). >>> cheers, >>> R?mi >>> [1] [ https://bugs.openjdk.java.net/browse/JDK-8244556 | >>> https://bugs.openjdk.java.net/browse/JDK-8244556 ] -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Oct 24 20:45:15 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 24 Oct 2020 22:45:15 +0200 (CEST) Subject: Bad iteraction between the compact constructor and parameters captured by a lambda Message-ID: <201353862.2185257.1603572315938.JavaMail.zimbra@u-pem.fr> When we have decided to ban this.items = items in the compact constructor and ask our users to use an assignment instead, it has a stupid side effect to make the parameters inside the compact constructor not effectively final, so a lambda can not capture them. So a code like this doesn't compile :( record Matrix(int[][] items) { Matrix { var copy = new int[items.length]; Arrays.setAll(copy, i -> items[i].clone()); items = copy; // items is not effectively final anymore } } Obviouly, introducing an intermediary local variable works, but it's not pretty record Matrix(int[][] items) { Matrix { var copy = new int[items.length]; var items2 = items; Arrays.setAll(copy, i -> items2[i].clone()); items = copy; } } I'm not sure what we should do here :( I kind a like the current rules of a compact constructor. R?mi From forax at univ-mlv.fr Sat Oct 24 20:48:44 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 24 Oct 2020 22:48:44 +0200 (CEST) Subject: Bad iteraction between the compact constructor and parameters captured by a lambda In-Reply-To: <201353862.2185257.1603572315938.JavaMail.zimbra@u-pem.fr> References: <201353862.2185257.1603572315938.JavaMail.zimbra@u-pem.fr> Message-ID: <1769057966.2185631.1603572524468.JavaMail.zimbra@u-pem.fr> Oops, instead of var copy = new int[items.length]; it should be var copy = new int[items.length][]; I've fixed the codes below. ----- Mail original ----- > De: "Remi Forax" > ?: "amber-spec-experts" > Envoy?: Samedi 24 Octobre 2020 22:45:15 > Objet: Bad iteraction between the compact constructor and parameters captured by a lambda > When we have decided to ban > this.items = items > in the compact constructor and ask our users to use an assignment instead, > it has a stupid side effect to make the parameters inside the compact > constructor not effectively final, so a lambda can not capture them. > > So a code like this doesn't compile :( > > record Matrix(int[][] items) { > Matrix { > var copy = new int[items.length][]; > Arrays.setAll(copy, i -> items[i].clone()); > items = copy; // items is not effectively final anymore > } > } > > > > Obviouly, introducing an intermediary local variable works, but it's not pretty > > record Matrix(int[][] items) { > Matrix { > var copy = new int[items.length][]; > var items2 = items; > Arrays.setAll(copy, i -> items2[i].clone()); > items = copy; > } > } > > > I'm not sure what we should do here :( > I kind a like the current rules of a compact constructor. > > R?mi From vicente.romero at oracle.com Sat Oct 24 21:40:58 2020 From: vicente.romero at oracle.com (Vicente Romero) Date: Sat, 24 Oct 2020 17:40:58 -0400 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> Message-ID: Hi, The name of the method is still: permittedSubclasses Vicente On 10/24/20 7:35 AM, forax at univ-mlv.fr wrote: > Ok nice, > I suppose permittedSubclasses has been renamed to > getPermittedSubclasses at the same time. > > R?mi > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"Gavin Bierman" , "Remi Forax" > > *Cc: *"amber-spec-experts" , > "joe darcy" > *Envoy?: *Vendredi 23 Octobre 2020 17:36:44 > *Objet: *Re: getPermittedSubclasses() on j.l.rClass returning an > array of ClassDesc > > FTR: this was largely a "for consistency" decision, because > nestmates does it the same way. (Which is to say, it was a > deliberate suboptimal choice aimed at minimizing the number of API > idioms that users of reflection had to deal with.) > > On 10/23/2020 11:27 AM, Gavin Bierman wrote: > > Just to follow this up; we have decided to change the signature of permittedSubclasses to the following: > > public Class[] permittedSubclasses() {} > > Thanks! > Gavin > > On 8 May 2020, at 23:53, Remi Forax wrote: > > The current draft of the reflection API for the sealed keyword adds a method getPermittedSubclasses() [1] to java.lang.Class. > > I'm not fully sure that returning an array of ClassDesc is the right choice here, mainly for two reasons, > > 1/ it's weird to return an array of ClassDesc when all others similar methods return an array of Class, > I know why a ClassDesc might be "better" because it avoid the class loading, > but it also means that now to fully understand java.lang.Class, people has to understand how java.lang.constant works. > The java.lang.constant API was not designed for that, the first line of the overview of this package talks about descriptors, constant pool and indy, not something beginners should worry about. > > 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error prone from a user POV, to resolve a ClassDesc to a class, the user as to provide a Lookup > and there is a good chance that users will pick the wrong ones. The number of people that understand classloading and how Lookup works is < 10, > even experts struggle given the number of time the Lookup API as to be patched in recent years. Returning a ClassDesc in this context is like asking a child > to read the serial number of a loaded gun. > Perhaps a way to mitigate that is to provide the code a user should use to get the equivalent classes in the javadoc of getPermittedSubclasses(). > > cheers, > R?mi > > [1]https://bugs.openjdk.java.net/browse/JDK-8244556 > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Oct 24 21:47:42 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 24 Oct 2020 17:47:42 -0400 Subject: Bad iteraction between the compact constructor and parameters captured by a lambda In-Reply-To: <201353862.2185257.1603572315938.JavaMail.zimbra@u-pem.fr> References: <201353862.2185257.1603572315938.JavaMail.zimbra@u-pem.fr> Message-ID: On 10/24/2020 4:45 PM, Remi Forax wrote: > When we have decided to ban > this.items = items > in the compact constructor and ask our users to use an assignment instead, > it has a stupid side effect to make the parameters inside the compact constructor not effectively final, so a lambda can not capture them. > > So a code like this doesn't compile :( > > record Matrix(int[][] items) { > Matrix { > var copy = new int[items.length]; > Arrays.setAll(copy, i -> items[i].clone()); > items = copy; // items is not effectively final anymore > } > } But, is this any different than capturing any other constructor parameter that you mutate in the body? From forax at univ-mlv.fr Sat Oct 24 22:29:16 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 25 Oct 2020 00:29:16 +0200 (CEST) Subject: Bad iteraction between the compact constructor and parameters captured by a lambda In-Reply-To: References: <201353862.2185257.1603572315938.JavaMail.zimbra@u-pem.fr> Message-ID: <70129874.2193994.1603578556486.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Remi Forax" , "amber-spec-experts" > Envoy?: Samedi 24 Octobre 2020 23:47:42 > Objet: Re: Bad iteraction between the compact constructor and parameters captured by a lambda > On 10/24/2020 4:45 PM, Remi Forax wrote: >> When we have decided to ban >> this.items = items >> in the compact constructor and ask our users to use an assignment instead, >> it has a stupid side effect to make the parameters inside the compact >> constructor not effectively final, so a lambda can not capture them. >> >> So a code like this doesn't compile :( >> >> record Matrix(int[][] items) { >> Matrix { >> var copy = new int[items.length]; >> Arrays.setAll(copy, i -> items[i].clone()); >> items = copy; // items is not effectively final anymore >> } >> } > > But, is this any different than capturing any other constructor > parameter that you mutate in the body? Nope, but you don't have to re-assign the parameter is a plain old constructor, you can use this.parameter = parameter; In a compact constructor, if you do a defensive copy, you have too change the value of the parameter. R?mi From forax at univ-mlv.fr Sat Oct 24 22:32:22 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 25 Oct 2020 00:32:22 +0200 (CEST) Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> Message-ID: <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> Hi Vicente, the 'get' was removed because the method was not returning Class objects, now that permittedSubclasses behave like the other methods of java.lang.Class, the name should be changed back to "get..." to reflect that. R?mi > De: "Vicente Romero" > ?: "Remi Forax" , "Brian Goetz" , > "Gavin Bierman" > Cc: "amber-spec-experts" , "joe darcy" > > Envoy?: Samedi 24 Octobre 2020 23:40:58 > Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of > ClassDesc > Hi, > The name of the method is still: permittedSubclasses > Vicente > On 10/24/20 7:35 AM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote: >> Ok nice, >> I suppose permittedSubclasses has been renamed to getPermittedSubclasses at the >> same time. >> R?mi >>> De: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>> ?: "Gavin Bierman" [ mailto:gavin.bierman at oracle.com | >>> ] , "Remi Forax" [ mailto:forax at univ-mlv.fr | >>> ] >>> Cc: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>> ] , "joe darcy" [ >>> mailto:joe.darcy at oracle.com | ] >>> Envoy?: Vendredi 23 Octobre 2020 17:36:44 >>> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of >>> ClassDesc >>> FTR: this was largely a "for consistency" decision, because nestmates does it >>> the same way. (Which is to say, it was a deliberate suboptimal choice aimed at >>> minimizing the number of API idioms that users of reflection had to deal with.) >>> On 10/23/2020 11:27 AM, Gavin Bierman wrote: >>>> Just to follow this up; we have decided to change the signature of >>>> permittedSubclasses to the following: >>>> public Class[] permittedSubclasses() {} >>>> Thanks! >>>> Gavin >>>>> On 8 May 2020, at 23:53, Remi Forax [ mailto:forax at univ-mlv.fr | >>>>> ] wrote: >>>>> The current draft of the reflection API for the sealed keyword adds a method >>>>> getPermittedSubclasses() [1] to java.lang.Class. >>>>> I'm not fully sure that returning an array of ClassDesc is the right choice >>>>> here, mainly for two reasons, >>>>> 1/ it's weird to return an array of ClassDesc when all others similar methods >>>>> return an array of Class, >>>>> I know why a ClassDesc might be "better" because it avoid the class loading, >>>>> but it also means that now to fully understand java.lang.Class, people has to >>>>> understand how java.lang.constant works. >>>>> The java.lang.constant API was not designed for that, the first line of the >>>>> overview of this package talks about descriptors, constant pool and indy, not >>>>> something beginners should worry about. >>>>> 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error >>>>> prone from a user POV, to resolve a ClassDesc to a class, the user as to >>>>> provide a Lookup >>>>> and there is a good chance that users will pick the wrong ones. The number of >>>>> people that understand classloading and how Lookup works is < 10, >>>>> even experts struggle given the number of time the Lookup API as to be patched >>>>> in recent years. Returning a ClassDesc in this context is like asking a child >>>>> to read the serial number of a loaded gun. >>>>> Perhaps a way to mitigate that is to provide the code a user should use to get >>>>> the equivalent classes in the javadoc of getPermittedSubclasses(). >>>>> cheers, >>>>> R?mi >>>>> [1] [ https://bugs.openjdk.java.net/browse/JDK-8244556 | >>>>> https://bugs.openjdk.java.net/browse/JDK-8244556 ] -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sun Oct 25 01:18:06 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sun, 25 Oct 2020 08:18:06 +0700 Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: Hello! I'm a little bit scared of introducing this kind of implicit cast into the assignment operation. This looks like the right operand in MyFooImpl mfi = f; becomes a poly-expression, as the type of the f expression now depends on the mfi type. How will it interact with other constructs? E.g. can we write MyFooImpl getFooImpl(MyFoo f) {return f;}? Or aMethodAcceptingFooImpl(f)? What about something that requires more inference, like Function = f -> f; ? I believe this would be either a very special rule applicable to very specific constructors (e.g. variable declaration only) or it will overcomplicate the type inference, possibly introducing new constructs where all the types cannot be unambiguously inferred. In the former case, this could be confusing. People would ask: why it works in declarations but doesn't work in assignments/method calls/returns/lambdas? If we want a compile-time check that 'this cast is safe as there's exactly one implementor', I think this should be done in a more explicit way. Btw we already designed such a way: MyFooImpl mfi = switch(f) {case MyFooImpl _mfi -> _mfi;}; This would do the thing, though may look somewhat ugly. We can add some sugar if anybody doesn't like this syntax. Probably like MyFooImpl mfi = (safe-cast-to MyFooImpl)f; But my point is that this kind of downcast should be explicit. I'm also not sure that this construct will be necessary so often. First, it's for internal use only, so the surface is quite limited: the clients don't need it. Second, even in internal uses, it's likely that in most of the cases the public API of interface Foo will be enough, so the downcast won't be necessary. Third, it would not be so hard to create a private one-liner method for this: private class MyFooImpl { private static MyFooImpl asImpl(Foo f) { return switch (f) {case MyFooImpl mfi -> mfi;}; } } This will keep a compile-time check and allow downcasting in any context. E.g. even in qualifiers, like asImpl(f).methodThatExistsInImplOnly(). I don't think we should overcomplicate the language here. With best regards, Tagir Valeev. On Fri, Oct 9, 2020 at 10:16 PM Brian Goetz wrote: > > Here's an idea that I've been thinking about for a few days, it's not urgent to decide on now, but I think it is worth considering in the background. > > When we did expression switch, we had an interesting discussion about what is the point of not writing a default clause on an optimistically total enum switch (and the same reasoning applies on switches on sealed types.) Suppose I have: > > var x = switch (trafficLight) { > case RED -> ... > case YELLOW -> ... > case GREEN -> ... > } > > People like this because they don't have to write a silly default clause that just throws an silly exception with a silly message (and as a bonus, is hard to cover with tests.) But Kevin pointed out that this is really the lesser benefit of the compiler reasoning about exhaustiveness; the greater benefit is that it allows you to more precisely capture assumptions in your program about totality, which the compiler can validate for you. If later, someone adds BLUE to traffic lights, the above switch fails to recompile, and we are constructively informed about an assumption being violated, whereas if we had a default clause, the fact that our assumption went stale gets swept under the rug. > > I was writing some code with sealed classes the other day, and I discovered an analogue of this which we may want to consider. I had: > > public sealed interface Foo > permits MyFooImpl { } > private class MyFooImpl implements Foo { } > > which I think we can agree will be a common enough pattern. And I found myself wanting to write: > > void m(Foo f) { > MyFooImpl mfi = (MyFooImpl) f; > ... > } > > This line of code is based on the assumption that Foo is sealed to permit only MyFooImpl, which is a valid assumption right now, since all this code exists only on my workstation. But some day, someone else may extend Foo to permit two private implementations, but may not be aware of the time bombs I've buried here. > > Suppose, though, that U were assignable to T if U is a sealed type and all permitted subtypes of U are assignable to T. Then I'd be able to write: > > MyFooImpl mfi = f; > > Not only do I not have to write the cast (the minor benefit), but rather than burying the assumption "all implementations of Foo are castable to MyFooImpl" in implementation code that can only fail at runtime, I can capture it in a way the compiler can verify on every recompilation, and when the underlying assumption is invalidated, so is the code that makes the assumption. This seems less brittle (the major benefit.) > > This generalizes, of course. Suppose we have: > > sealed interface X permits A, B { } > class A extends Base implements X { } > class B extends Base implements X { } > > Then X becomes assignable to Base. > > I'm not quite sure yet how to feel about this, but I really do like the idea of being able to put the assumptions like "X must be a Y" -- which people _will_ make -- in a place where the compiler can typecheck it. > > > > > > From amaembo at gmail.com Sun Oct 25 01:29:17 2020 From: amaembo at gmail.com (Tagir Valeev) Date: Sun, 25 Oct 2020 08:29:17 +0700 Subject: [sealed] @SafeVarargs on the methods of sealed class/interface Message-ID: Hello! As suggested by Lukas Eder on Twitter ( https://mobile.twitter.com/lukaseder/status/1318890756250193920 ), it looks a good idea to allow SafeVarargs on the methods of sealed classes/interfaces. The rules could be the following: Check all the permitted subtypes. For every permitted subtype: - If it has this method overridden/implemented, then it must also be annotated as SafeVarargs - Otherwise, if the subtype is non-sealed, it's an error - Otherwise, the procedure is repeated for all the permitted subtypes of this subtype. What do you think? With best regards, Tagir Valeev. From forax at univ-mlv.fr Sun Oct 25 12:35:39 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 25 Oct 2020 13:35:39 +0100 (CET) Subject: [sealed] @SafeVarargs on the methods of sealed class/interface In-Reply-To: References: Message-ID: <1007759200.2371956.1603629339426.JavaMail.zimbra@u-pem.fr> It means that safevarargs becomes a flag that needs to be verified by the VM because of separate compilation. The current rules for @SafeVarargs avoid that by disallowing @SafeVarargs on instance method (the ones that can be overriden), so the effect of @SafeVarargs is only local. I'm not sure that changing the semantics of @SafeVarargs is a good idea because fundamentally @SafeVarargs as a work around the fact that arrays are mutable [*]. In the future, if we have immutable arrays (Array 2.0) and a way to support varargs (...) for such arrays, @SafeVarargs becomes useless. regards, R?mi [*] or covariant, but it's too late for that bit ----- Mail original ----- > De: "Tagir Valeev" > ?: "amber-spec-experts" > Envoy?: Dimanche 25 Octobre 2020 02:29:17 > Objet: [sealed] @SafeVarargs on the methods of sealed class/interface > Hello! > > As suggested by Lukas Eder on Twitter ( > https://mobile.twitter.com/lukaseder/status/1318890756250193920 ), it > looks a good idea to allow SafeVarargs on the methods of sealed > classes/interfaces. The rules could be the following: > Check all the permitted subtypes. For every permitted subtype: > - If it has this method overridden/implemented, then it must also be > annotated as SafeVarargs > - Otherwise, if the subtype is non-sealed, it's an error > - Otherwise, the procedure is repeated for all the permitted subtypes > of this subtype. > > What do you think? > > With best regards, > Tagir Valeev. From forax at univ-mlv.fr Sun Oct 25 12:55:38 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 25 Oct 2020 13:55:38 +0100 (CET) Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: <641421192.2374947.1603630538314.JavaMail.zimbra@u-pem.fr> I've not answered to Brian series of emails because i'm not able to figure out if it's a good thing or not. One thing to know that Brian did not say is that we may need to see a sealed super type as a subtype for Valhalla. Currently generics doesn't support primitive object type (ex inline type), like it doesn't support 'int'. So the compiler generates a sealed super type which is a reference type that can be used instead (not unlike Integer is used instead of int). If we have a rule that allows semless conversions between the super reference type and primitive object type, the pair super reference type/primitive object type will behave more or less like the auto-boxing rules between an Integer and an int. Now, Tagir, you have talk about about having a special syntax or using a switch at use site, there is also another solution which is to declare a special relationship at declaration site. sealed interface MyFoo permits MyFooImpl { } autobox class MyFooImpl implements MyFoo { } with autobox (or whatever keyword you want) that means - verifies that i'm the only implementation - allow unboxing conversion from MyFoo to MyFooImpl You can see autobox as a kind of handsake between the sealed type and its sole implementation that this is the only implementation. One issue I see with this proposal, and the original one by Brian, the relation is asymetric, MyFooImpl to MyFoo is subtyping but MyFoo to MyFooImpl is unboxing (so not the same pass when finding the most specific method), so it doesn't work fully like Integer and int. And i've used unboxing because it's already an existing rule, but maybe it's a new kind of conversion that will require us to carefully think about regards, R?mi ----- Mail original ----- > De: "Tagir Valeev" > ?: "Brian Goetz" > Cc: "amber-spec-experts" > Envoy?: Dimanche 25 Octobre 2020 02:18:06 > Objet: Re: Relaxed assignment conversions for sealed types > Hello! > > I'm a little bit scared of introducing this kind of implicit cast into > the assignment operation. This looks like the right operand in > MyFooImpl mfi = f; becomes a poly-expression, as the type of the f > expression now depends on the mfi type. How will it interact with > other constructs? E.g. can we write MyFooImpl getFooImpl(MyFoo f) > {return f;}? Or aMethodAcceptingFooImpl(f)? What about something that > requires more inference, like Function = f -> f; ? > > I believe this would be either a very special rule applicable to very > specific constructors (e.g. variable declaration only) or it will > overcomplicate the type inference, possibly introducing new constructs > where all the types cannot be unambiguously inferred. In the former > case, this could be confusing. People would ask: why it works in > declarations but doesn't work in assignments/method > calls/returns/lambdas? > > If we want a compile-time check that 'this cast is safe as there's > exactly one implementor', I think this should be done in a more > explicit way. Btw we already designed such a way: > MyFooImpl mfi = switch(f) {case MyFooImpl _mfi -> _mfi;}; > This would do the thing, though may look somewhat ugly. We can add > some sugar if anybody doesn't like this syntax. Probably like > MyFooImpl mfi = (safe-cast-to MyFooImpl)f; > But my point is that this kind of downcast should be explicit. > > I'm also not sure that this construct will be necessary so often. > First, it's for internal use only, so the surface is quite limited: > the clients don't need it. Second, even in internal uses, it's likely > that in most of the cases the public API of interface Foo will be > enough, so the downcast won't be necessary. Third, it would not be so > hard to create a private one-liner method for this: > > private class MyFooImpl { > private static MyFooImpl asImpl(Foo f) { > return switch (f) {case MyFooImpl mfi -> mfi;}; > } > } > > This will keep a compile-time check and allow downcasting in any > context. E.g. even in qualifiers, like > asImpl(f).methodThatExistsInImplOnly(). > > I don't think we should overcomplicate the language here. > > With best regards, > Tagir Valeev. > > On Fri, Oct 9, 2020 at 10:16 PM Brian Goetz wrote: >> >> Here's an idea that I've been thinking about for a few days, it's not urgent to >> decide on now, but I think it is worth considering in the background. >> >> When we did expression switch, we had an interesting discussion about what is >> the point of not writing a default clause on an optimistically total enum >> switch (and the same reasoning applies on switches on sealed types.) Suppose I >> have: >> >> var x = switch (trafficLight) { >> case RED -> ... >> case YELLOW -> ... >> case GREEN -> ... >> } >> >> People like this because they don't have to write a silly default clause that >> just throws an silly exception with a silly message (and as a bonus, is hard to >> cover with tests.) But Kevin pointed out that this is really the lesser >> benefit of the compiler reasoning about exhaustiveness; the greater benefit is >> that it allows you to more precisely capture assumptions in your program about >> totality, which the compiler can validate for you. If later, someone adds BLUE >> to traffic lights, the above switch fails to recompile, and we are >> constructively informed about an assumption being violated, whereas if we had a >> default clause, the fact that our assumption went stale gets swept under the >> rug. >> >> I was writing some code with sealed classes the other day, and I discovered an >> analogue of this which we may want to consider. I had: >> >> public sealed interface Foo >> permits MyFooImpl { } >> private class MyFooImpl implements Foo { } >> >> which I think we can agree will be a common enough pattern. And I found myself >> wanting to write: >> >> void m(Foo f) { >> MyFooImpl mfi = (MyFooImpl) f; >> ... >> } >> >> This line of code is based on the assumption that Foo is sealed to permit only >> MyFooImpl, which is a valid assumption right now, since all this code exists >> only on my workstation. But some day, someone else may extend Foo to permit >> two private implementations, but may not be aware of the time bombs I've buried >> here. >> >> Suppose, though, that U were assignable to T if U is a sealed type and all >> permitted subtypes of U are assignable to T. Then I'd be able to write: >> >> MyFooImpl mfi = f; >> >> Not only do I not have to write the cast (the minor benefit), but rather than >> burying the assumption "all implementations of Foo are castable to MyFooImpl" >> in implementation code that can only fail at runtime, I can capture it in a way >> the compiler can verify on every recompilation, and when the underlying >> assumption is invalidated, so is the code that makes the assumption. This >> seems less brittle (the major benefit.) >> >> This generalizes, of course. Suppose we have: >> >> sealed interface X permits A, B { } >> class A extends Base implements X { } >> class B extends Base implements X { } >> >> Then X becomes assignable to Base. >> >> I'm not quite sure yet how to feel about this, but I really do like the idea of >> being able to put the assumptions like "X must be a Y" -- which people _will_ >> make -- in a place where the compiler can typecheck it. >> >> >> >> >> From brian.goetz at oracle.com Sun Oct 25 14:06:42 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 25 Oct 2020 10:06:42 -0400 Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: You are right to be a little scared.? I'm a little scared too. Many language features are proposed on the basis of "wouldn't it be cool if we could write this!"?? This is not one of those cases.? The motivation here is purely: wouldn't people write _safer_ code with this. The motivation came out of observing how sealed classes are likely to be used.? I was working on a library, which intends to make heavy use of sealing, where it exposes public interfaces and has a single private implementation for almost all of them.? The implementations are strongly coupled, they want freely access each other. What I found was that it was _overwhelmingly_ tempting -- and I'm pretty disciplined -- to do things like: ??? void foo(Bar b) { ??????? BarImpl bi = (BarImpl) b; ??????? bi.internalGoop(); ??? } instead of: ??? void foo(Bar b) { ??????? if (b instanceof BarImpl bi) { ???? ? ?? ? bi.internalGoop(); ??? ?? } ?????? else ??????????? throw new IllegalStateException(blah blah); ??? } This is just like the do-nothing default clause, if not worse. My concern is NOT that I don't want to write the cast in the first example.? It's that that correctness of the cast relies on an assumption that the compiler can't type check.? So we leave users with some bad choices: ?- Write the full-blown if-else version.? Which will annoy the users when they do, but more likely, they just won't do it. ?- Write the cast version.? Now our code is embedded with a hidden assumption, which could be later broken, and the compiler can't type check it for us. To make it clear that I'm not talking about the annoyance of typing the cast, let's pretend I'm suggesting to write it like this: ??? BarImpl bi = (__static BarImpl) b; which means "if this cast can't be statically proven true, give me a compile error."? I think we can agree that this version is strictly better than the first version (same runtime semantics, but more static type checking), and probably better than the second version too.? We can debate the syntax, but this is the point I want to discuss -- that we can make the language _safer_ by exploiting information the compiler has, as long as we don't sweep it under the rug. We want people to write APIs like this -- where the public face is abstract interfaces, but the private face can be efficient, encapsulated code.? And we also want to provide the maximum type checking we can for that code. (Digression: In the future, people might have a slightly better option than the if-else: ??? switch (b) { ??????? case BarImpl bi -> { rest of method } ??? } which will benefit from the exhaustive type checking.? But this will require patterns in switch (coming) and, for statement switches, some sort of totality opt in.? End digression.) Let's try and separate the "oooh, scary and different" reactions here from the problem.? I think this is a problem that we should try to solve, and we _have the information in hand_ to solve it.? What remains is mostly a user-psychology exercise of "what is the right level of implicit/explicit here." Some comments inline. > I'm a little bit scared of introducing this kind of implicit cast into > the assignment operation. This looks like the right operand in > MyFooImpl mfi = f; becomes a poly-expression, as the type of the f > expression now depends on the mfi type. How will it interact with > other constructs? E.g. can we write MyFooImpl getFooImpl(MyFoo f) > {return f;}? Or aMethodAcceptingFooImpl(f)? As I sketched it, yes; this would be an assignability thing, where Foo is assignable to FooImpl by virtue of sealing.? (Again, if sealed types were true union types, this would already be true by virtue of the definition of subtyping for a union type.) > What about something that > requires more inference, like Function = f -> f; ? Is this any different from: ??? Function = n -> n; We rewrite with inference variables: ??? Function = (\alpha n) -> n; and gather constraints that says we can assign Integer to \alpha and assign \alpha to Number, and inference says "OK, Integer." > If we want a compile-time check that 'this cast is safe as there's > exactly one implementor', I think this should be done in a more > explicit way. Btw we already designed such a way: > MyFooImpl mfi = switch(f) {case MyFooImpl _mfi -> _mfi;}; > This would do the thing, though may look somewhat ugly. We can add > some sugar if anybody doesn't like this syntax. Probably like > MyFooImpl mfi = (safe-cast-to MyFooImpl)f; > But my point is that this kind of downcast should be explicit. As mentioned above, I'm open to that.? I think the switch route is a little too indirect; it either won't occur to users, or it will still be too many characters to type.? My concern is that the path of least resistance here will lead to the blind cast; if we make it slightly more fussy, people may accept that, but if its too fussy, we'll just get more unsafe code. > I'm also not sure that this construct will be necessary so often. Well, I've got a 25,000 line prototype in front of me that wants to do this _all over the place_.? I want this code to be maintainable when I'm done with it.? I may know which casts are safe and which are not, but I don't want to rely on that.? So I think it will be an issue. > First, it's for internal use only, so the surface is quite limited: > the clients don't need it. Second, even in internal uses, it's likely > that in most of the cases the public API of interface Foo will be > enough, so the downcast won't be necessary. Third, it would not be so > hard to create a private one-liner method for this: > > private class MyFooImpl { > private static MyFooImpl asImpl(Foo f) { > return switch (f) {case MyFooImpl mfi -> mfi;}; > } > } (ObSnark: not so hard, times 25,000.) More seriously, I don't object to this as a possibility, but we're at least two language features away from being able to write this, patterns in switch, and total statement switches. On to Remi's comments: > If we have a rule that allows semless conversions between the super reference type and primitive object type, > the pair super reference type/primitive object type will behave more or less like the auto-boxing rules between an Integer and an int. That's exactly how I think it's going to work; we generalize boxing/unboxing to "value widening/narrowing" conversions, with the exact same semantics, and apply to all value/ref projections of inlines.? This neatly flows into rules like overload selection, where candidates without conversion are preferred to candidates with conversion.? No new rules to learn; just more uniform boxing. (Reminder: this is the Amber list, so take disagreements with this statement to the Valhalla list.) > One issue I see with this proposal, and the original one by Brian, the relation is asymetric, > MyFooImpl to MyFoo is subtyping but MyFoo to MyFooImpl is unboxing (so not the same pass when finding the most specific method), so it doesn't work fully like Integer and int. > And i've used unboxing because it's already an existing rule, but maybe it's a new kind of conversion that will require us to carefully think about Mucking up the declaration site for this seems unnecessary, and also limits the usefulness of it.? (As proposed, this can work when you have multiple subtypes that implement a common interface.)? But, the part of this that is worth discussing is that yes, this could be a _conversion_ rather than assignability.? The effect of this would mostly show up in overload selection (where the strongly applicable options are preferred over the loosely ones) and type inference. Its a possibility, but I'm not sure it really buys much, and doesn't really address Tagir's "too magic" concerns.? (If anything, conversions are more magic than assignability.) From forax at univ-mlv.fr Sun Oct 25 17:13:38 2020 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 25 Oct 2020 18:13:38 +0100 (CET) Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: <1285021438.2416560.1603646018967.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Brian Goetz" > ?: "Tagir Valeev" > Cc: "amber-spec-experts" > Envoy?: Dimanche 25 Octobre 2020 15:06:42 > Objet: Re: Relaxed assignment conversions for sealed types [...] > > On to Remi's comments: > >> If we have a rule that allows semless conversions between the super reference >> type and primitive object type, >> the pair super reference type/primitive object type will behave more or less >> like the auto-boxing rules between an Integer and an int. > > That's exactly how I think it's going to work; we generalize > boxing/unboxing to "value widening/narrowing" conversions, with the > exact same semantics, and apply to all value/ref projections of > inlines.? This neatly flows into rules like overload selection, where > candidates without conversion are preferred to candidates with > conversion.? No new rules to learn; just more uniform boxing. (Reminder: > this is the Amber list, so take disagreements with this statement to the > Valhalla list.) I don't disagree, i mention Valhalla primitive object type on this list to provide context to Tagir. > >> One issue I see with this proposal, and the original one by Brian, the relation >> is asymetric, >> MyFooImpl to MyFoo is subtyping but MyFoo to MyFooImpl is unboxing (so not the >> same pass when finding the most specific method), so it doesn't work fully like >> Integer and int. >> And i've used unboxing because it's already an existing rule, but maybe it's a >> new kind of conversion that will require us to carefully think about > > Mucking up the declaration site for this seems unnecessary, and also > limits the usefulness of it.? (As proposed, this can work when you have > multiple subtypes that implement a common interface.) yep, using the declaration site is more restrictive but because it's a handshake if someone add a new permitted type, the code will not compile at the declaration of the sealed type and not somewhere else in the application. >? But, the part of this that is worth discussing is that yes, this could be a _conversion_ > rather than assignability.? The effect of this would mostly show up in > overload selection (where the strongly applicable options are preferred > over the loosely ones) and type inference. Its a possibility, but I'm > not sure it really buys much, and doesn't really address Tagir's "too > magic" concerns.? (If anything, conversions are more magic than > assignability.) I've proposed boxing because i fear that if subtyping and this new conversion are at the same level, we will have trouble with inference because you can go in both direction at the same time. R?mi From brian.goetz at oracle.com Sun Oct 25 17:50:27 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 25 Oct 2020 13:50:27 -0400 Subject: Relaxed assignment conversions for sealed types In-Reply-To: <1285021438.2416560.1603646018967.JavaMail.zimbra@u-pem.fr> References: <1285021438.2416560.1603646018967.JavaMail.zimbra@u-pem.fr> Message-ID: <3e174ced-18ce-1e20-98be-e24d9a102950@oracle.com> >> ? But, the part of this that is worth discussing is that yes, this could be a _conversion_ >> rather than assignability.? The effect of this would mostly show up in >> overload selection (where the strongly applicable options are preferred >> over the loosely ones) and type inference. Its a possibility, but I'm >> not sure it really buys much, and doesn't really address Tagir's "too >> magic" concerns.? (If anything, conversions are more magic than >> assignability.) > I've proposed boxing because i fear that if subtyping and this new conversion are at the same level, we will have trouble with inference because you can go in both direction at the same time. > Sure, it's a reasonable thing to consider.? But I worry also we're getting lost in the details of _how_, and the more important conversation right now is the "what language are we building" one. We know we have a construct that (a) has the seeds of better type checking in it, and (b) will tempt people to do things that are not as type safe as we can make them.? I'm skeptical that "do nothing" here is the right move. Since I still can't avoid having a paint at the bikeshed, I'll point out that a sneaky vector by which we could address this is to lean on our new friend, pattern totality.? Suppose we have: ??? sealed abstract class C permits D { } ??? final class D extends C { } We have already said that the type pattern `D d` is total on C (with some remainder.)? We're already considering pattern assignment for total patterns.? For example, if Foo(...) is a deconstruction pattern on Foo, then the following: ??? Foo(var x, var y) = aFoo; would be a valid statement, it would deconstruct the RHS, bind variables x and y, and throw on the remainder (which in this case includes null.)? Now, note that ??? D d = c; could be interpreted as a local variable declaration, _or_ as a pattern match if the type pattern `D d` is total on C with some remainder.? (It is no accident that a type pattern and a variable declaration look alike.) Now, one could reasonably object that this might be a clever pun, but that the users won't get the joke, since it does look so much like an assignment.? In the past, we had toyed with giving this statement some more obvious syntax, like: ??? let D d = c; but over time this felt more and more like a Stroustrup's Rule artifact (new syntax should LOOK DIFFERENT!)? Further, we'd like to have a path to using patterns in things like method declarations: ??? void foo(Point(var x, var y) p) { ... x, y, and p are all bound here... } which is a pretty natural way to use total patterns (once users are ready for this, they surely are not now.)? Requring that unconditional-bind be a statement ("let") rather than something more fluid puts roadblocks to getting there. So, to summarize, another vector by which we could get there is to say that ??? D d = c; is a pattern match of a total pattern (D d) on the operand c.? This would mean we could do this assignment for locals, but wouldn't do anything for us in method invocation context.? That doesn't sound immediately better, but its a direction to consider. But, in general: let's try to converge on the goals before we dive too deep into details. From brian.goetz at oracle.com Sun Oct 25 21:22:03 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 25 Oct 2020 17:22:03 -0400 Subject: [sealed] @SafeVarargs on the methods of sealed class/interface In-Reply-To: References: Message-ID: <954ac3f0-17c5-4fd4-262a-f6e11f5a1f1f@oracle.com> I think this is a great idea for IDEs to have as an inspection :) Inheritance of annotations, or anything that even smells like it, is a siren song (e.g., what happens when something is inherited from two interfaces, differently annotated?)? And further, I cannot recall a single tweak to annotations that has not given rise to "but you do X here, surely you have to do Y there."? So it's a double whammy -- we don't get a good return, plus we encourage more demand for doing more of the same kind of bad investment. To me, the return on this investment _in the language_ seems dramatically negative.? (But for IDEs, it might be great.) On 10/24/2020 9:29 PM, Tagir Valeev wrote: > Hello! > > As suggested by Lukas Eder on Twitter ( > https://mobile.twitter.com/lukaseder/status/1318890756250193920 ), it > looks a good idea to allow SafeVarargs on the methods of sealed > classes/interfaces. The rules could be the following: > Check all the permitted subtypes. For every permitted subtype: > - If it has this method overridden/implemented, then it must also be > annotated as SafeVarargs > - Otherwise, if the subtype is non-sealed, it's an error > - Otherwise, the procedure is repeated for all the permitted subtypes > of this subtype. > > What do you think? > > With best regards, > Tagir Valeev. -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Mon Oct 26 03:54:12 2020 From: john.r.rose at oracle.com (John Rose) Date: Sun, 25 Oct 2020 20:54:12 -0700 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> Message-ID: <4C6CFBDD-CBE3-4542-9CBE-8FF5D5CAA892@oracle.com> On Oct 24, 2020, at 3:32 PM, forax at univ-mlv.fr wrote: > > the 'get' was removed because the method was not returning Class objects, now that permittedSubclasses behave like the other methods of java.lang.Class, > the name should be changed back to "get..." to reflect that. One reason (beyond good old consistency) to use the old name with the old type: If we ever upgrade the return types from array to List, we?ll want to use new method names. Using more than one naming convention for these methods reduces options for choosing a successor naming convention. (Assuming we don?t have overload selection on return types, which I view as a very tempting idea but one that has yet to show a killer use case.) From forax at univ-mlv.fr Mon Oct 26 11:04:40 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 26 Oct 2020 12:04:40 +0100 (CET) Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <4C6CFBDD-CBE3-4542-9CBE-8FF5D5CAA892@oracle.com> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> <4C6CFBDD-CBE3-4542-9CBE-8FF5D5CAA892@oracle.com> Message-ID: <263303930.310925.1603710280560.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "John Rose" > ?: "Remi Forax" > Cc: "Vicente Romero" , "Brian Goetz" , "Gavin Bierman" > , "amber-spec-experts" , "joe darcy" > > Envoy?: Lundi 26 Octobre 2020 04:54:12 > Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc > On Oct 24, 2020, at 3:32 PM, forax at univ-mlv.fr wrote: >> >> the 'get' was removed because the method was not returning Class objects, now >> that permittedSubclasses behave like the other methods of java.lang.Class, >> the name should be changed back to "get..." to reflect that. > > One reason (beyond good old consistency) to use the old > name with the old type: If we ever upgrade the return types > from array to List, we?ll want to use new method names. > Using more than one naming convention for these methods > reduces options for choosing a successor naming convention. I believe it's better to introduce a new interface in between Collection and List, let say Array and refactor Object[]/int[]/... to implement Array. > > (Assuming we don?t have overload selection on return > types, which I view as a very tempting idea but one that > has yet to show a killer use case.) It's OT but there are several reasons for using the return type in the overload selection are - the inference is using the return type but not when overloading, many students find this weird (as they find weird to have contravariance of generics using wildcards but not contravariance of method parameter types when overriding) - plain old ad-hoc polymorphism class Defaults { static int empty() { return 0; } static double empty() { return 0.0; } ... static T empty() { return null; } } - be able to express covariant bridges in Java I do not think there is a killer use case in this list anyway. regards, R?mi From vicente.romero at oracle.com Mon Oct 26 14:15:46 2020 From: vicente.romero at oracle.com (Vicente Romero) Date: Mon, 26 Oct 2020 10:15:46 -0400 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> Message-ID: Hi Remi, I will update the name, adding the `get` back, Thanks, Vicente On 10/24/20 6:32 PM, forax at univ-mlv.fr wrote: > Hi Vicente, > > the 'get' was removed because the method was not returning Class > objects, now that permittedSubclasses behave like the other methods of > java.lang.Class, > the name should be changed back to "get..." to reflect that. > > R?mi > > ------------------------------------------------------------------------ > > *De: *"Vicente Romero" > *?: *"Remi Forax" , "Brian Goetz" > , "Gavin Bierman" > *Cc: *"amber-spec-experts" , > "joe darcy" > *Envoy?: *Samedi 24 Octobre 2020 23:40:58 > *Objet: *Re: getPermittedSubclasses() on j.l.rClass returning an > array of ClassDesc > > Hi, > > The name of the method is still: permittedSubclasses > > Vicente > > On 10/24/20 7:35 AM, forax at univ-mlv.fr wrote: > > Ok nice, > I suppose permittedSubclasses has been renamed to > getPermittedSubclasses at the same time. > > R?mi > > ------------------------------------------------------------------------ > > *De: *"Brian Goetz" > *?: *"Gavin Bierman" , "Remi > Forax" > *Cc: *"amber-spec-experts" > , "joe darcy" > > *Envoy?: *Vendredi 23 Octobre 2020 17:36:44 > *Objet: *Re: getPermittedSubclasses() on j.l.rClass > returning an array of ClassDesc > > FTR: this was largely a "for consistency" decision, > because nestmates does it the same way.? (Which is to say, > it was a deliberate suboptimal choice aimed at minimizing > the number of API idioms that users of reflection had to > deal with.) > > On 10/23/2020 11:27 AM, Gavin Bierman wrote: > > Just to follow this up; we have decided to change the signature of permittedSubclasses to the following: > > public Class[] permittedSubclasses() {} > > Thanks! > Gavin > > On 8 May 2020, at 23:53, Remi Forax wrote: > > The current draft of the reflection API for the sealed keyword adds a method getPermittedSubclasses() [1] to java.lang.Class. > > I'm not fully sure that returning an array of ClassDesc is the right choice here, mainly for two reasons, > > 1/ it's weird to return an array of ClassDesc when all others similar methods return an array of Class, > I know why a ClassDesc might be "better" because it avoid the class loading, > but it also means that now to fully understand java.lang.Class, people has to understand how java.lang.constant works. > The java.lang.constant API was not designed for that, the first line of the overview of this package talks about descriptors, constant pool and indy, not something beginners should worry about. > > 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error prone from a user POV, to resolve a ClassDesc to a class, the user as to provide a Lookup > and there is a good chance that users will pick the wrong ones. The number of people that understand classloading and how Lookup works is < 10, > even experts struggle given the number of time the Lookup API as to be patched in recent years. Returning a ClassDesc in this context is like asking a child > to read the serial number of a loaded gun. > Perhaps a way to mitigate that is to provide the code a user should use to get the equivalent classes in the javadoc of getPermittedSubclasses(). > > cheers, > R?mi > > [1]https://bugs.openjdk.java.net/browse/JDK-8244556 > > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 26 15:50:48 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 26 Oct 2020 16:50:48 +0100 (CET) Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> Message-ID: <260155124.461670.1603727448120.JavaMail.zimbra@u-pem.fr> > De: "Vicente Romero" > ?: "Remi Forax" > Cc: "Brian Goetz" , "Gavin Bierman" > , "amber-spec-experts" > , "joe darcy" > Envoy?: Lundi 26 Octobre 2020 15:15:46 > Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of > ClassDesc > Hi Remi, > I will update the name, adding the `get` back, > Thanks, > Vicente Many thanks, R?mi > On 10/24/20 6:32 PM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote: >> Hi Vicente, >> the 'get' was removed because the method was not returning Class objects, now >> that permittedSubclasses behave like the other methods of java.lang.Class, >> the name should be changed back to "get..." to reflect that. >> R?mi >>> De: "Vicente Romero" [ mailto:vicente.romero at oracle.com | >>> ] >>> ?: "Remi Forax" [ mailto:forax at univ-mlv.fr | ] , "Brian >>> Goetz" [ mailto:brian.goetz at oracle.com | ] , "Gavin >>> Bierman" [ mailto:gavin.bierman at oracle.com | ] >>> Cc: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>> ] , "joe darcy" [ >>> mailto:joe.darcy at oracle.com | ] >>> Envoy?: Samedi 24 Octobre 2020 23:40:58 >>> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of >>> ClassDesc >>> Hi, >>> The name of the method is still: permittedSubclasses >>> Vicente >>> On 10/24/20 7:35 AM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote: >>>> Ok nice, >>>> I suppose permittedSubclasses has been renamed to getPermittedSubclasses at the >>>> same time. >>>> R?mi >>>>> De: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>>>> ?: "Gavin Bierman" [ mailto:gavin.bierman at oracle.com | >>>>> ] , "Remi Forax" [ mailto:forax at univ-mlv.fr | >>>>> ] >>>>> Cc: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>>>> ] , "joe darcy" [ >>>>> mailto:joe.darcy at oracle.com | ] >>>>> Envoy?: Vendredi 23 Octobre 2020 17:36:44 >>>>> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of >>>>> ClassDesc >>>>> FTR: this was largely a "for consistency" decision, because nestmates does it >>>>> the same way. (Which is to say, it was a deliberate suboptimal choice aimed at >>>>> minimizing the number of API idioms that users of reflection had to deal with.) >>>>> On 10/23/2020 11:27 AM, Gavin Bierman wrote: >>>>>> Just to follow this up; we have decided to change the signature of >>>>>> permittedSubclasses to the following: >>>>>> public Class[] permittedSubclasses() {} >>>>>> Thanks! >>>>>> Gavin >>>>>>> On 8 May 2020, at 23:53, Remi Forax [ mailto:forax at univ-mlv.fr | >>>>>>> ] wrote: >>>>>>> The current draft of the reflection API for the sealed keyword adds a method >>>>>>> getPermittedSubclasses() [1] to java.lang.Class. >>>>>>> I'm not fully sure that returning an array of ClassDesc is the right choice >>>>>>> here, mainly for two reasons, >>>>>>> 1/ it's weird to return an array of ClassDesc when all others similar methods >>>>>>> return an array of Class, >>>>>>> I know why a ClassDesc might be "better" because it avoid the class loading, >>>>>>> but it also means that now to fully understand java.lang.Class, people has to >>>>>>> understand how java.lang.constant works. >>>>>>> The java.lang.constant API was not designed for that, the first line of the >>>>>>> overview of this package talks about descriptors, constant pool and indy, not >>>>>>> something beginners should worry about. >>>>>>> 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error >>>>>>> prone from a user POV, to resolve a ClassDesc to a class, the user as to >>>>>>> provide a Lookup >>>>>>> and there is a good chance that users will pick the wrong ones. The number of >>>>>>> people that understand classloading and how Lookup works is < 10, >>>>>>> even experts struggle given the number of time the Lookup API as to be patched >>>>>>> in recent years. Returning a ClassDesc in this context is like asking a child >>>>>>> to read the serial number of a loaded gun. >>>>>>> Perhaps a way to mitigate that is to provide the code a user should use to get >>>>>>> the equivalent classes in the javadoc of getPermittedSubclasses(). >>>>>>> cheers, >>>>>>> R?mi >>>>>>> [1] [ https://bugs.openjdk.java.net/browse/JDK-8244556 | >>>>>>> https://bugs.openjdk.java.net/browse/JDK-8244556 ] -------------- next part -------------- An HTML attachment was scrubbed... URL: From chris.hegarty at oracle.com Tue Oct 27 11:45:13 2020 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Tue, 27 Oct 2020 11:45:13 +0000 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> Message-ID: <5088088E-CBA5-4853-BA9B-3272F3C1F66F@oracle.com> While I agree with all comments in this thread so far, I have one additional comment relating to the type parameter. Since the set of permitted classes must be subclasses of T, should the declaration be: public Class[] getPermittedSubclasses() { .. } -Chris. > On 26 Oct 2020, at 14:15, Vicente Romero wrote: > > Hi Remi, > > I will update the name, adding the `get` back, > > Thanks, > Vicente > > On 10/24/20 6:32 PM, forax at univ-mlv.fr wrote: >> Hi Vicente, >> >> the 'get' was removed because the method was not returning Class objects, now that permittedSubclasses behave like the other methods of java.lang.Class, >> the name should be changed back to "get..." to reflect that. >> >> R?mi >> >> De: "Vicente Romero" >> ?: "Remi Forax" , "Brian Goetz" , "Gavin Bierman" >> Cc: "amber-spec-experts" , "joe darcy" >> Envoy?: Samedi 24 Octobre 2020 23:40:58 >> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc >> Hi, >> >> The name of the method is still: permittedSubclasses >> >> Vicente >> >> On 10/24/20 7:35 AM, forax at univ-mlv.fr wrote: >> Ok nice, >> I suppose permittedSubclasses has been renamed to getPermittedSubclasses at the same time. >> >> R?mi >> >> De: "Brian Goetz" >> ?: "Gavin Bierman" , "Remi Forax" >> Cc: "amber-spec-experts" , "joe darcy" >> Envoy?: Vendredi 23 Octobre 2020 17:36:44 >> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc >> FTR: this was largely a "for consistency" decision, because nestmates does it the same way. (Which is to say, it was a deliberate suboptimal choice aimed at minimizing the number of API idioms that users of reflection had to deal with.) >> >> On 10/23/2020 11:27 AM, Gavin Bierman wrote: >> Just to follow this up; we have decided to change the signature of permittedSubclasses to the following: >> >> public Class[] permittedSubclasses() {} >> >> Thanks! >> Gavin >> >> >> On 8 May 2020, at 23:53, Remi Forax >> wrote: >> >> The current draft of the reflection API for the sealed keyword adds a method getPermittedSubclasses() [1] to java.lang.Class. >> >> I'm not fully sure that returning an array of ClassDesc is the right choice here, mainly for two reasons, >> >> 1/ it's weird to return an array of ClassDesc when all others similar methods return an array of Class, >> I know why a ClassDesc might be "better" because it avoid the class loading, >> but it also means that now to fully understand java.lang.Class, people has to understand how java.lang.constant works. >> The java.lang.constant API was not designed for that, the first line of the overview of this package talks about descriptors, constant pool and indy, not something beginners should worry about. >> >> 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error prone from a user POV, to resolve a ClassDesc to a class, the user as to provide a Lookup >> and there is a good chance that users will pick the wrong ones. The number of people that understand classloading and how Lookup works is < 10, >> even experts struggle given the number of time the Lookup API as to be patched in recent years. Returning a ClassDesc in this context is like asking a child >> to read the serial number of a loaded gun. >> Perhaps a way to mitigate that is to provide the code a user should use to get the equivalent classes in the javadoc of getPermittedSubclasses(). >> >> cheers, >> R?mi >> >> [1] >> https://bugs.openjdk.java.net/browse/JDK-8244556 >> >> >> >> > From forax at univ-mlv.fr Tue Oct 27 11:51:42 2020 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 27 Oct 2020 12:51:42 +0100 (CET) Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <5088088E-CBA5-4853-BA9B-3272F3C1F66F@oracle.com> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> <5088088E-CBA5-4853-BA9B-3272F3C1F66F@oracle.com> Message-ID: <1491892860.769152.1603799502470.JavaMail.zimbra@u-pem.fr> ----- Mail original ----- > De: "Chris Hegarty" > ?: "Vicente Romero" > Cc: "Remi Forax" , "amber-spec-experts" > Envoy?: Mardi 27 Octobre 2020 12:45:13 > Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc > While I agree with all comments in this thread so far, I have one additional > comment relating to the type parameter. > > Since the set of permitted classes must be subclasses of T, should the > declaration be: > > public Class[] getPermittedSubclasses() { .. } Hi Chris, in theory yes, in practice, an array of parametrized types is usually unsafe, in this peculiar case, it's always unsafe because you can write Class[] array = Itf.class.getPermittedSubclasses(); Object[] array2 = array; array2[0] = Object.class; Itf itf = array[0].newInstance(); // CCE, the compiler insert a cast to Itf and Object doesn't implement Itf > > > -Chris. R?mi > > >> On 26 Oct 2020, at 14:15, Vicente Romero wrote: >> >> Hi Remi, >> >> I will update the name, adding the `get` back, >> >> Thanks, >> Vicente >> >> On 10/24/20 6:32 PM, forax at univ-mlv.fr wrote: >>> Hi Vicente, >>> >>> the 'get' was removed because the method was not returning Class objects, now >>> that permittedSubclasses behave like the other methods of java.lang.Class, >>> the name should be changed back to "get..." to reflect that. >>> >>> R?mi >>> >>> De: "Vicente Romero" >>> ?: "Remi Forax" , "Brian Goetz" , >>> "Gavin Bierman" >>> Cc: "amber-spec-experts" , "joe darcy" >>> >>> Envoy?: Samedi 24 Octobre 2020 23:40:58 >>> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of >>> ClassDesc >>> Hi, >>> >>> The name of the method is still: permittedSubclasses >>> >>> Vicente >>> >>> On 10/24/20 7:35 AM, forax at univ-mlv.fr wrote: >>> Ok nice, >>> I suppose permittedSubclasses has been renamed to getPermittedSubclasses at the >>> same time. >>> >>> R?mi >>> >>> De: "Brian Goetz" >>> ?: "Gavin Bierman" , "Remi Forax" >>> Cc: "amber-spec-experts" , "joe darcy" >>> >>> Envoy?: Vendredi 23 Octobre 2020 17:36:44 >>> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of >>> ClassDesc >>> FTR: this was largely a "for consistency" decision, because nestmates does it >>> the same way. (Which is to say, it was a deliberate suboptimal choice aimed at >>> minimizing the number of API idioms that users of reflection had to deal with.) >>> >>> On 10/23/2020 11:27 AM, Gavin Bierman wrote: >>> Just to follow this up; we have decided to change the signature of >>> permittedSubclasses to the following: >>> >>> public Class[] permittedSubclasses() {} >>> >>> Thanks! >>> Gavin >>> >>> >>> On 8 May 2020, at 23:53, Remi Forax >>> wrote: >>> >>> The current draft of the reflection API for the sealed keyword adds a method >>> getPermittedSubclasses() [1] to java.lang.Class. >>> >>> I'm not fully sure that returning an array of ClassDesc is the right choice >>> here, mainly for two reasons, >>> >>> 1/ it's weird to return an array of ClassDesc when all others similar methods >>> return an array of Class, >>> I know why a ClassDesc might be "better" because it avoid the class loading, >>> but it also means that now to fully understand java.lang.Class, people has to >>> understand how java.lang.constant works. >>> The java.lang.constant API was not designed for that, the first line of the >>> overview of this package talks about descriptors, constant pool and indy, not >>> something beginners should worry about. >>> >>> 2/ returning a symbolic view (ClassDesc) instead of a Class is *very* error >>> prone from a user POV, to resolve a ClassDesc to a class, the user as to >>> provide a Lookup >>> and there is a good chance that users will pick the wrong ones. The number of >>> people that understand classloading and how Lookup works is < 10, >>> even experts struggle given the number of time the Lookup API as to be patched >>> in recent years. Returning a ClassDesc in this context is like asking a child >>> to read the serial number of a loaded gun. >>> Perhaps a way to mitigate that is to provide the code a user should use to get >>> the equivalent classes in the javadoc of getPermittedSubclasses(). >>> >>> cheers, >>> R?mi >>> >>> [1] >>> https://bugs.openjdk.java.net/browse/JDK-8244556 >>> >>> >>> >>> From chris.hegarty at oracle.com Tue Oct 27 16:56:45 2020 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Tue, 27 Oct 2020 16:56:45 +0000 Subject: [sealed-classes] Spec for next version of Sealed Classes In-Reply-To: References: Message-ID: <8B135BD4-664A-4D0B-A092-24D0CD7265F4@oracle.com> Gavin, Comments relating to the JVMS: (Many of my comments relate to the wellformedness of the PermittedSubclasses Attribute, which may or may not be strictly specified, so take with a pinch of salt! ;-) ) I find myself mapping the set of allowable values in the PermittedSubclasses Attribute to the behaviour of the Core Reflection API, to discern; what is enforced by the VM and what is possible to be observed reflective at run-time. The `number_of_classes` is not required to be a non-zero, but I expect that it should. Or maybe this is implicit or specified/enforced elsewhere? "Array items that do not attempt to directly extend or implement the current class or interface are ignored.? - Ok, so such items can be from any runtime module or package, right? Related to `number_of_classes`, the requirement is really that there is at least one non-ignored item, no? ( if enforced at all ) The recent change (proposed on this list) to Class::getPermittedSubclasses, means that it will no longer be possible to reflectively return permitted subclasses that are not loaded, or more specifically ?loadable" - the classes must exist somewhere. Currently, in JDK 15, permittedSubclasses will return class descriptors for non-loadable classes. I think that this is ok, we just need to ensure that it fits into the other rules here. "C does not have its ACC_PUBLIC flag set (4.1) and the superclass belongs to a different run-time package than C.? - I get that there is a bidirectional accessibility relationship between the superclass and the subclass, but this seems at odds with JEP text: ?In particular, a subclass may be less accessible than the sealed class?. Why is this not that the superclass must have ACC_PUBLIC, and not the subclass? Duplicate entries in the `classes` array are effectively ignored? They must be elided by Class::getPermittedSubclasses, right? Or can duplicates be propagated through this API point? Comments relating to the JVLS: Trivial type in 5.1 - "This is *is* the underlying notion used in the definition of narrowing reference conversion. " "Changing a class that is declared non-sealed to no longer be declared non-sealed does not break compatibility with pre-existing binaries.? - doesn?t this depend on the whether or not the superclass/superinterface is sealed? And whether it has subclasses or not ? -Chris. > On 23 Oct 2020, at 16:16, Gavin Bierman wrote: > > Dear all: > > Drafts of the specs for the Sealed Classes feature that we plan to preview for > a second time in JDK 16 are now available: > > http://cr.openjdk.java.net/~gbierman/8246775/latest/ > > [NB: The URL will change once we have a JEP number, and will be announced.] > > The changes are the same as those in the first preview that was released in Java > SE 15, except for minor editorial changes and the following: > > - Clarification the use of context when applying the lexical grammar, > particularly in the identification of contextual keywords (formerly described > as "restricted identifiers" and "restricted keywords"). This is detailed in a > companion document entitled ?Contextual Keywords". The keywords `sealed`, > `non-sealed`, and `permits` are now defined as new instances of contextual > keywords (3.9). > > - This spec now assumes that the changes detailed in companion documents > entitled "Consistent Class and Interface Terminology? and "Local and Nested > Static Declarations" have been applied (these are being introduced as part of > the Records JEP). In particular, this means that Java SE 16 will support > static declarations in two new positions: > > 1. Local, implicitly-static interfaces and enum classes > 2. Static members of inner classes > > This requires asserting that local interfaces are not permitted to be > `sealed`. (14.3) > > - To enhance narrowing reference conversion to allow for stricter checking of > cast conversions with respect to sealed type hierarchies (5.1.6.1). > > - Local classes are not considered when determining implicitly declared > permitted direct subclasses of a `sealed` class or `sealed` interface > (8.1.6, 9.1.4). > > > Comments welcome! > > Gavin From chris.hegarty at oracle.com Tue Oct 27 17:15:16 2020 From: chris.hegarty at oracle.com (Chris Hegarty) Date: Tue, 27 Oct 2020 17:15:16 +0000 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <1491892860.769152.1603799502470.JavaMail.zimbra@u-pem.fr> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> <5088088E-CBA5-4853-BA9B-3272F3C1F66F@oracle.com> <1491892860.769152.1603799502470.JavaMail.zimbra@u-pem.fr> Message-ID: > On 27 Oct 2020, at 11:51, forax at univ-mlv.fr wrote: > >> ... >> Since the set of permitted classes must be subclasses of T, should the >> declaration be: >> >> public Class[] getPermittedSubclasses() { .. } > > > Hi Chris, > in theory yes, > in practice, an array of parametrized types is usually unsafe, in this peculiar case, it's always unsafe because you can write > > Class[] array = Itf.class.getPermittedSubclasses(); > Object[] array2 = array; > array2[0] = Object.class; > > Itf itf = array[0].newInstance(); // CCE, the compiler insert a cast to Itf and Object doesn't implement Itf Thanks for the explanation Remi - I fell afoul of generics 101 ;-) -Chris. -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Thu Oct 29 21:29:09 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 29 Oct 2020 15:29:09 -0600 Subject: [sealed-classes] Spec for next version of Sealed Classes In-Reply-To: <8B135BD4-664A-4D0B-A092-24D0CD7265F4@oracle.com> References: <8B135BD4-664A-4D0B-A092-24D0CD7265F4@oracle.com> Message-ID: <2DBCF829-E5CA-44B7-9C9C-81B093CB7704@oracle.com> > On Oct 27, 2020, at 10:56 AM, Chris Hegarty wrote: > > Gavin, > > Comments relating to the JVMS: > > (Many of my comments relate to the wellformedness of the PermittedSubclasses Attribute, which may or may not be strictly specified, so take with a pinch of salt! ;-) ) > > I find myself mapping the set of allowable values in the PermittedSubclasses Attribute to the behaviour of the Core Reflection API, to discern; what is enforced by the VM and what is possible to be observed reflective at run-time. > > The `number_of_classes` is not required to be a non-zero, but I expect that it should. Or maybe this is implicit or specified/enforced elsewhere? The language requires the 'permits' clause to be non-empty, but there's no such JVM restriction. Naturally, if you permit no subclasses, nobody can extend you. (This is sort of like declaring a private constructor.) > "Array items that do not attempt to directly extend or implement the current class or interface are ignored.? - Ok, so such items can be from any runtime module or package, right? Related to `number_of_classes`, the requirement is really that there is at least one non-ignored item, no? ( if enforced at all ) Like 'NestMembers', you can put whatever garbage names you want in here. They'll just have no effect. The only validation is when a class asks permission to extend. (Separately, reflection can do whatever it thinks is best.) > The recent change (proposed on this list) to Class::getPermittedSubclasses, means that it will no longer be possible to reflectively return permitted subclasses that are not loaded, or more specifically ?loadable" - the classes must exist somewhere. Currently, in JDK 15, permittedSubclasses will return class descriptors for non-loadable classes. I think that this is ok, we just need to ensure that it fits into the other rules here. Yep. I think this is a good change, and I think there's nothing wrong with the reflection API either ignoring some entries or reporting class loading errors. Best to do whatever the NestMembers query does. > "C does not have its ACC_PUBLIC flag set (4.1) and the superclass belongs to a different run-time package than C.? - I get that there is a bidirectional accessibility relationship between the superclass and the subclass, but this seems at odds with JEP text: ?In particular, a subclass may be less accessible than the sealed class?. Why is this not that the superclass must have ACC_PUBLIC, and not the subclass? The goal of this rule is to simulate resolution without actually asking to perform it, since the class hasn't even been created yet. Part of resolution is access checking, so we simulate it here. The JEP says "a permitted subclass and its sealed superclass must be accessible to each other. However, permitted subclasses need not have the same accessibility as each other, or as the sealed class." Keep in mind we're talking now about language-level accessibility, which doesn't always map to JVM accessibility. Anyway, some examples: public super, non-public sub, same package package-access super, private sub, same package public *and exported* super, public sub private super, public sub, same nest > Duplicate entries in the `classes` array are effectively ignored? They must be elided by Class::getPermittedSubclasses, right? Or can duplicates be propagated through this API point? No JVM restrictions on this. Again, reflection should do something reasonable, probably matching what NestMembers does. > "Changing a class that is declared non-sealed to no longer be declared non-sealed does not break compatibility with pre-existing binaries.? - doesn?t this depend on the whether or not the superclass/superinterface is sealed? And whether it has subclasses or not ? Agree, this seems like a mistake. If it's not `non-sealed`, it's becoming either `final` or `sealed`. Either way, that risks binary incompatibilities. From daniel.smith at oracle.com Thu Oct 29 21:38:08 2020 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 29 Oct 2020 15:38:08 -0600 Subject: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc In-Reply-To: <1491892860.769152.1603799502470.JavaMail.zimbra@u-pem.fr> References: <1800371247.929985.1588978383614.JavaMail.zimbra@u-pem.fr> <6333A7B0-144A-40A0-80A2-AF6778BF24F6@oracle.com> <8efdb9b2-939f-7c44-0940-7aad547c4c3a@oracle.com> <2025935165.2103166.1603539326260.JavaMail.zimbra@u-pem.fr> <1649379363.2194178.1603578742907.JavaMail.zimbra@u-pem.fr> <5088088E-CBA5-4853-BA9B-3272F3C1F66F@oracle.com> <1491892860.769152.1603799502470.JavaMail.zimbra@u-pem.fr> Message-ID: > On Oct 27, 2020, at 5:51 AM, forax at univ-mlv.fr wrote: > > ----- Mail original ----- >> De: "Chris Hegarty" >> ?: "Vicente Romero" >> Cc: "Remi Forax" , "amber-spec-experts" >> Envoy?: Mardi 27 Octobre 2020 12:45:13 >> Objet: Re: getPermittedSubclasses() on j.l.rClass returning an array of ClassDesc > >> While I agree with all comments in this thread so far, I have one additional >> comment relating to the type parameter. >> >> Since the set of permitted classes must be subclasses of T, should the >> declaration be: >> >> public Class[] getPermittedSubclasses() { .. } > > > Hi Chris, > in theory yes, > in practice, an array of parametrized types is usually unsafe, in this peculiar case, it's always unsafe because you can write > > Class[] array = Itf.class.getPermittedSubclasses(); > Object[] array2 = array; > array2[0] = Object.class; > > Itf itf = array[0].newInstance(); // CCE, the compiler insert a cast to Itf and Object doesn't implement Itf You're not wrong, but I'm not sure this is reason not to provide better typing information. It certainly is the contract of the 'getPermittedSubclasses' method not to pollute it with non-subclasses of T. That said, Class[] seems to be the precedent followed by other methods: Class getSuperclass() vs. Class[] getInterfaces() Constructor getConstructor(Class) vs. Constructor[] getConstructors() So I guess we should do the same with 'getPermittedSubclasses'. From brian.goetz at oracle.com Sat Oct 31 23:30:10 2020 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 31 Oct 2020 19:30:10 -0400 Subject: Relaxed assignment conversions for sealed types In-Reply-To: References: Message-ID: <4F38821E-C785-450D-BC2C-811A997B172E@oracle.com> > On Oct 25, 2020, at 10:06 AM, Brian Goetz wrote: > > To make it clear that I'm not talking about the annoyance of typing the cast, let's pretend I'm suggesting to write it like this: > > BarImpl bi = (__static BarImpl) b; Pulling on this string some more ? I think there?s a connection between this feature and total statement switches. We?ve been looking for a way to make statement switches total, and here, what we?re looking for is a way to make _casts_ total. Which suggests this is one feature, not two. So perhaps: switch-total (x) { ? } // a switch, but with added bonus totality checking BarImpl b = (total BarImpl) bar // a cast, but with added bonus totality checking Obviously we can use another word besides `total`, but it?s a pretty good straw man. -------------- next part -------------- An HTML attachment was scrubbed... URL: