<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class="">I think that you are going to be better served by making things more explicit for non-exhaustive enums with binary compatibility concerns. I don’t think that there is a solution that salvages an ordering for a jump table while still allowing open enums to change during the same @availability version without at some point having the coder editing the enum needing to define or override the case tags (e.g. the "@abi(2)" workaround).&nbsp;</div><div class=""><br class=""></div><div class="">So given that it’s going to come up and [Apple] library authors will need to deal with it, why not use the same rules/logic/syntax that already exists for RawRepresentable Int enums? I.e. tag value is the same as order in the enum, except that you can override it and following values by explicitly specifying. People familiar with C are going to already know instinctively how to correctly modify the enum to maintain the ABI when reordering it. That is,&nbsp;</div><div class=""><br class=""></div><div class="">public enum SpellKind {<br class="">&nbsp; case hex = 0<br class="">&nbsp; case charm<br class="">&nbsp; case curse<br class="">&nbsp; case jinx = 4<br class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class="">&nbsp; case summoning</div><div class="">&nbsp; case banishing</div><div class=""><br class=""></div><div class="">&nbsp; // deprecated, it’s more of a curse targeted on a locale than it is a separate kind</div><div class="">&nbsp; case blight = 3</div><div class="">}</div><div class=""><br class=""></div><div class="">This doesn’t help any for the protocol witness example, of course. Adding “= 4” to the end of your function declaration in your protocol might looks a little odd. But for enums, at least, a large number of devs are going to just get it, and you automatically get the correct behavior (by explicitly defining “public enum SpellKind: Int”) for making this enum work for Objective-C inter-op.</div><div class=""><br class=""></div><div class="">- Greg</div></div></div><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class=""><br class=""></div></blockquote></div></div><div><blockquote type="cite" class=""><div class="">On Sep 29, 2017, at 6:21 PM, Jordan Rose via swift-dev &lt;<a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hello again, swift-dev! This is a sort of follow-up to "What can you change in a fixed-contents struct" from a few weeks ago, but this time concerning enums. Worryingly, this seems to be an important consideration even for non-exhaustive enums, which are normally the ones where we'd want to allow a developer to do anything and everything that doesn't break source compatibility.<div class=""><br class=""></div><div class="">[<b class="">This only affects libraries with binary compatibility concerns</b>. Libraries distributed with an app can always allow the app to access the enum's representation directly. That makes this an Apple-centric problem in the near term.]<br class=""><div class=""><br class=""></div><div class="">So, what's the issue? <b class="">We want to make it efficient to switch over a non-exhaustive enum</b>, even from a client library that doesn't have access to the enum's guts. We do this by asking for the enum's tag separately from its payload (pseudocode):</div></div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">switch getMyOpaqueEnumTag(&amp;myOpaqueEnum) {</div><div class="">case 0:</div><div class="">&nbsp; var payload: Int</div><div class="">&nbsp; getMyOpaqueEnumPayload(&amp;myOpaqueEnum, 0, &amp;payload)</div><div class="">&nbsp; doSomething(payload)</div><div class="">case 1:</div><div class="">&nbsp; var payload: String</div><div class="">&nbsp; getMyOpaqueEnumPayload(&amp;myOpaqueEnum, 1, &amp;payload)</div><div class="">&nbsp; doSomethingElse(payload)</div><div class="">default:</div><div class="">&nbsp; print("unknown case")</div><div class="">}</div></blockquote><br class=""><div class="">The tricky part is those constant values "0" and "1". We'd really like them to be constants so that the calling code can actually emit a jump table rather than a series of chained conditionals, but that means <b class="">case tags are part of the ABI, even for non-exhaustive enums</b>.</div><div class=""><br class=""></div><div class="">Like with struct layout, this means we need a stable ordering for cases. Since non-exhaustive enums can have new cases added at any time, we can't do a simple alphabetical sort, nor can we do some kind of ordering on the payload types. The naive answer, then, is that <b class="">enum&nbsp;cases cannot be reordered, even in non-exhaustive enums</b>. This isn't great, because people like being able to move deprecated enum cases to the end of the list, where they're out of the way, but it's at least explainable, and consistent with the idea of enums some day having a 'cases' property that includes all cases.</div><div class=""><br class=""></div><div class="">Slava and I aren't happy with this, but we haven't thought of another solution yet. The rest of this email will describe our previous idea, which has a fatal flaw.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">Availability Ordering</b></div><div class=""><b class=""><br class=""></b></div><div class="">In a library with binary compatibility concerns, any new API that gets added should always be explicitly annotated with an availability attribute. Today that looks like this:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">@available(macOS 10.13, iOS 11, tvOS 11, watchOS 4, *)</div></blockquote><div class=""><br class=""></div><div class="">It's a model we only support for Apple platforms, but in theory it's extendable to arbitrary "deployments". You ought to be able to say `@available(MagicKit 5)` and have the compiler actually check that.</div><div class=""><br class=""></div><div class="">Let's say we had this model, and we were using it like this:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">public enum SpellKind {</div><div class="">&nbsp; case hex</div><div class="">&nbsp; case charm</div><div class="">&nbsp; case curse</div><div class="">&nbsp; @available(MagicKit 5)</div><div class="">&nbsp; case blight</div><div class="">&nbsp; @available(MagicKit 5.1)</div><div class="">&nbsp; case jinx</div><div class="">}</div></blockquote><div class=""><br class=""></div><div class="">"Availability ordering" says that we can derive a canonical ordering from the names of cases (which are API) combined with their versions. Since we "know" that newly-added cases will always have a newer version than existing cases, we can just put the older cases first. In this case, that would give us a canonical ordering of [charm, curse, hex, blight, jinx].</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">The Fatal Flaw</b></div><div class=""><b class=""><br class=""></b></div><div class="">It's time for MagicKit 6 to come out, and we're going to add a new SpellKind:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">@available(MagicKit 6)</div><div class="">case summoning</div><div class="">// [charm, curse, hex, blight, jinx, summoning]</div></blockquote><br class=""><div class="">We ship out a beta to our biggest clients, but realize we forgot a vital feature. Beta 2 comes with another new SpellKind:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">@available(MagicKit 6)</div><div class="">case banishing</div></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class="">// [charm, curse, hex, blight, jinx, banishing, summoning]</blockquote><br class=""><div class="">And now we're in trouble: anything built against the first beta expects 'summoning' to have tag 5, not 6. Our clients have to recompile everything before they can even try out the new version of the library.</div><div class=""><br class=""></div><div class="">Can this be fixed? Sure. We could add support for beta versions to `@available`, or fake it somehow with the existing version syntax. But in both of these cases, it means you have to know what constitutes a "release", so that you can be sure to use a higher number than the previous "release". This could be made to work for a single library, but falls down for an entire Apple OS. If the Foundation team wants to add a second new enum case while macOS is still in beta, they're not going to stop and recompile all of /System/Library/Frameworks just to try out their change.</div><div class=""><br class=""></div><div class="">So, availability ordering is great when you have easily divisible "releases", but falls down when you want to make a change "during a release".</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">Salvaging Availability Ordering?</b></div><div class=""><b class=""><br class=""></b></div><div class="">- We could still sort by availability, so that you can reorder the sections but not the individual cases in them. That doesn't seem very useful, though.</div><div class=""><br class=""></div><div class="">- We could say "this is probably rare", and state that anything added "in the same release" needs to get an explicit annotation for ordering purposes. (This is equivalent to the `@abi(2)` Dave Zarzycki mentioned in the previous thread—it's not the default but it's there if you need it.)</div><div class=""><br class=""></div><div class="">- We could actually require libraries to annotate all of their "releases", but in order to apply that within Apple we'd need some translation from library versions (like "Foundation&nbsp;1258") to OS versions ("macOS 10.11.4"), and then we'd <i class="">still</i>&nbsp;need to figure out what to do about betas. (And there's a twist, at least at Apple, where a release's version number isn't decided until the new source code is submitted.)</div><div class=""><br class=""></div><div class="">- There might be something clever that I haven't thought of yet.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">This kind of known ordering isn't just good for enum cases; it could also be applied to protocol witnesses, so that those could be directly dispatched like C++ vtables. (I don't think we want to restrict reordering of protocol requirements, as much as it would make our lives easier.) So if anyone has any brilliant ideas, Slava and I would love to hear them!</div><div class=""><br class=""></div><div class="">Jordan</div></div>_______________________________________________<br class="">swift-dev mailing list<br class=""><a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-dev<br class=""></div></blockquote></div><br class=""></body></html>