<div dir="auto">I'm not sure I understand the problem you present, are you saying that if I have the following (I'm on my phone, so I apologize about the formatting):<div dir="auto"><div dir="auto"><div dir="auto"><br></div><div dir="auto">sealed interface S permits A {}</div><div dir="auto"><br></div><div dir="auto">switch(x) {</div><div dir="auto">    case A a -> ...</div><div dir="auto">    case S s -> ...</div><div dir="auto">}</div><div dir="auto"><br></div><div dir="auto">Then when I modify S to be:</div><div dir="auto"><br></div><div dir="auto">sealed interface S permits A, B {}</div><div dir="auto"><br></div><div dir="auto">My switch expression now has a hidden case? I.e. the compiler doesn't help me know that I may want to add an explicit B case?</div><div dir="auto"><br></div><div dir="auto">I can see few problems with disallowing a "catchall" clause.</div><div dir="auto"><br></div><div dir="auto">Here is a textbook example of sealed types:</div><div dir="auto"><br></div><div dir="auto">sealed interface Json permits JPrimitive, JArray, JObject {}</div><div dir="auto"><br></div><div dir="auto">sealed interface JPrimitive<T> extends Json permits JNull, JBool, JNumber, JString {</div><div dir="auto">    T unbox();</div><div dir="auto">}</div><div dir="auto"><br></div><div dir="auto">// the implementation of unbox just returns the underlying object, for JNull it returns null.</div><div dir="auto">record JNull() implements JPrimitive<Void> {}</div><div dir="auto">record JBool(bool val) implements JPrimitive<Boolean> {}</div><div dir="auto">...</div><div dir="auto"><br></div><div dir="auto">Will:</div><div dir="auto"><br></div><div dir="auto">switch(x) {</div><div dir="auto">    JArray a -> ...</div><div dir="auto">    JObject o -> ...</div><div dir="auto">    JPrimitive<?> p -> ...</div><div dir="auto">}</div><div dir="auto"><br></div><div dir="auto">Be allowed? It has the same problem, if we extend the permits list of JPrimitive, the compiler won't notify you, but we really want a single "primitive" clause, semantically we know that it won't ever change (but the compiler has no way of knowing it).</div><div dir="auto"><br></div><div dir="auto">If you only care about the "first layer", there are still cases were you have a sealed types that permits quite a lot of types (say, 10, which is a lot but possible), you may have a function that receive that type, and handle 7 out of the 10 ways the same way, but 3 of the types require a special handling (think subtypes that requires external resources, or thread locks), not having a catchall will create a lot of boilerplate (or will make you switch to if-else chain (pun intended))</div><div dir="auto"><br></div><div dir="auto">---</div><div dir="auto"><br></div><div dir="auto">If you still think it is a problem, I'm sure you could make a rule in your linter/build pipeline to disallow catchall clauses</div><div dir="auto"><br></div></div></div><br><br><div class="gmail_quote" dir="auto"><div dir="ltr" class="gmail_attr">On Sat, Apr 8, 2023, 21:24 Robert <<a href="mailto:roberts14@cablelink.at" target="_blank" rel="noreferrer">roberts14@cablelink.at</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div>Hi all, newbie here.  </div><div><br></div><div>First off — loving Amber features.</div><div><br></div><div>Now, a *very narrow* observation about how JDK 20 handles sealed type selector expressions.</div><div>(Code snippet below.)</div><div><br></div><div>If a sealed type S later grows its list of permitted subtypes at some point, </div><div>how should the compiler handle switch blocks with S as selector?</div><div><br></div><div>In the absence of a catchall label (i.e. <span style="color:rgb(0,0,0)">“case default -> ...” or “case S s -> ...”) ,</span></div><div><span style="color:rgb(0,0,0)">the compiler already correctly issues an error that coverage is incomplete.</span></div><div><span style="color:rgb(0,0,0)"><br></span></div><div><span style="color:rgb(0,0,0)">But if the block ended with a catchall label, the compiler is silent; the best that</span></div><div><span style="color:rgb(0,0,0)">can be hoped-for is that the coder throws a defensive exception in the catchall label </span></div><div><span style="color:rgb(0,0,0)">(and hope that it is triggered in testing).</span></div><div><span style="color:rgb(0,0,0)"><br></span></div><div><div><font color="#000000"><span>Suggestion</span></font>:</div></div><div><br></div><div>Compiler should *disallows* catchall statements in switch blocks that use a sealed type </div><div><b style="font-style:normal"><u>in the switch selector expression</u></b>.  Note that this special case does not affect all the </div><div>other non-sealed-type selectors (e.g. Object o), and appears (to me) to strengthen the</div><div>safety-value of sealed types in this particular scenario (similar to enums).  </div><div><br></div><div><div>All in Java’s spirit of <span style="color:rgb(0,0,0)">“least surprise”.</span></div></div><div><br></div><div>Cheers, </div><div>Robert</div><div><br></div><div><br></div><div>PS. reported behavior confirmed using jshell in JDK 20:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div style="margin:0px;font-stretch:normal;line-height:normal;font-family:Menlo;color:rgb(0,0,0);font-size:18px"><span style="font-variant-ligatures:no-common-ligatures">jshell --enable-preview</span></div></div><div><div style="margin:0px;font-stretch:normal;line-height:normal;font-family:Menlo;color:rgb(0,0,0);font-size:18px"><span style="font-variant-ligatures:no-common-ligatures">|  Welcome to JShell -- Version 20</span></div></div><div><div style="margin:0px;font-stretch:normal;line-height:normal;font-family:Menlo;color:rgb(0,0,0);font-size:18px"><span style="font-variant-ligatures:no-common-ligatures">|  For an introduction type: /help intro</span></div></div></blockquote><div><br></div><div>on the following:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div>sealed interface S permits S.X, S.Y, S.Z, S.NEWBIE {</div><div><br></div><div>final class X implements S {};</div><div><br></div><div>final class Y implements S {};</div><div><br></div><div>final class Z implements S {};</div><div><br></div><div>final class NEWBIE implements S {};</div><div><br></div><div><span style="white-space:pre-wrap">   </span>public static void main(String [] sa) {</div><div><br></div><div><span style="white-space:pre-wrap"> </span>    S something = new NEWBIE();</div><div><span style="white-space:pre-wrap">        </span>    System.out.println(</div><div><span style="white-space:pre-wrap">                </span>switch (something) {</div><div><span style="white-space:pre-wrap">                     </span>case null -> 0;</div><div>        <span style="white-space:pre-wrap">           </span>case X x -> 1;</div><div>        <span style="white-space:pre-wrap">            </span>case Y y -> 2;</div><div>        <span style="white-space:pre-wrap">            </span>case Z z -> 3;</div><div><span style="white-space:pre-wrap">                        </span>case S s -> -1;</div><div><span style="white-space:pre-wrap">                       </span>// case default -> -1;</div><div><span style="white-space:pre-wrap">                </span>});</div><div>    <span style="white-space:pre-wrap">        </span>};</div><div>}</div></blockquote><div><br></div></div></blockquote></div></div>