<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="">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></body></html>