<div dir="ltr"><div><div>Over a year ago, we discussed adding a magic &quot;allValues&quot;/&quot;allCases&quot; static property on enums with a compiler-derived implementation. The original <a href="https://github.com/apple/swift-evolution/pull/114">proposal PR</a> has been reopened for Swift 5 after languishing for a while, and I&#39;d like to revisit it and make some changes before it goes up for formal review.</div></div><div><br></div><div>Prior discussion: <a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015098.html">https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015098.html</a> (good luck finding the rest of the thread if you weren&#39;t on the list at the time...)</div><div><br></div><div>[cc&#39;d swift-dev for importer/availability-related topics below.]</div><div><br></div><div><b>**Naming**</b><br></div><div><b><br></b></div><div>Given the complexity gap between a simple enumeration of cases and full support for non-enum types and associated values (which we don&#39;t intend to support with this proposal), I think it might be a good idea to adopt the names <b>CaseEnumerable/allCases</b> instead of ValueEnumerable/allValues.</div><div><br></div><div>The original proposal didn&#39;t expose allValues as a requirement, for fear of unduly restricting its type. However, if the protocol&#39;s scope is more limited, <b>static var allCases</b> can be exposed as a requirement since the implementations are not likely to be complex. Furthermore...</div><div><br></div><div><br></div><div><b>**Generics**</b></div><div><br></div><div>Since <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0142-associated-types-constraints.md">SE-0142</a> was implemented in Swift 4, we now have more expressive options for the protocol requirements:</div><div><br></div><div>  // 1 - array only</div><div>  protocol CaseEnumerable {</div><div>    static var allCases: [Self] { get }</div><div>  }</div><div><br></div><div>  // 2 - any sequence</div><div><div>  protocol CaseEnumerable {</div><div>    associatedtype <b>CaseSequence</b>: Sequence where CaseSequence.Element == Self</div><div>    static var <b>allCases</b>: CaseSequence { get }</div><div>  }</div></div><div><br></div><div>  // 3 - any collection</div><div><div>  protocol CaseEnumerable {</div><div>    associatedtype <b>CaseCollection</b>: Collection where CaseCollection.Element == Self</div><div>    static var <b>allCases</b>: CaseCollection { get }</div><div>  }</div></div><div><br></div><div>This restricts the CaseEnumerable protocol to be used as a generic constraint, but that&#39;d be true even with a plain array because of the Self type.</div><div><br></div><div>Personally I like the flexibility provided by the associatedtype, but I also recognize it won&#39;t be incredibly useful for enums — more so if we wanted to provide e.g. UInt8.allValues, whose ideal implementation might be &quot;return 0...UInt8.max&quot;. So I could see allowing allValues to be any sequence or collection, but flexibility for allCases might be less important. Others should weigh in here.</div><div><br></div><div><br></div><div><b>**Implementation strategy and edge cases**</b></div><div><div><br></div><div><a href="https://twitter.com/CodaFi_/status/920132464001024001">Last year</a>, Robert Widmann put together an implementation of CaseEnumerable: <a href="https://github.com/apple/swift/compare/master...CodaFi:ace-attorney">https://github.com/apple/swift/compare/master...CodaFi:ace-attorney</a> </div><div>I&#39;d love to hear from anyone more familiar with the code whether there&#39;s anything we&#39;d want to change about this approach.</div></div><div><br></div><div>A few tricky situations have been brought to my attention:</div><div><br></div><div>- Enums <b>imported from C/Obj-C</b> headers. Doug Gregor writes: <i>&quot;<span style="font-size:12.8px">The autogenerated allValues would only be able to </span><span class="gmail-il" style="font-size:12.8px">list</span><span style="font-size:12.8px"> the </span><span class="gmail-il" style="font-size:12.8px">enum</span><span style="font-size:12.8px"> </span><span class="gmail-il" style="font-size:12.8px">cases</span><span style="font-size:12.8px"> it knows about from the header it was compiled with. If the library changes to add </span><span class="gmail-il" style="font-size:12.8px">cases</span><span style="font-size:12.8px"> in the future (which, for example, Apple frameworks tend to do), those wouldn’t be captured in allValues.</span>&quot;</i></div><div><br></div><div>My understanding of the runtime/importer is very shallow, but with the current metadata-based strategy, I suspect imported enums couldn&#39;t be supported at all, or if they could, the metadata would be generated at import time rather than loaded dynamically from the library, which naturally wouldn&#39;t behave the same way when you drop in an upgraded version of the library. Is that correct?</div><div><br></div><div>(Nonetheless, if a user really wanted this auto-generation, it would be nice to allow it somehow. Personally, I have had enums whose &quot;source of truth&quot; was an Obj-C header file, but since it was compiled in with the rest of the application, we didn&#39;t care at all about library upgrades. Maybe an internal extension adding a conformance can be allowed to participate in auto-generation?)</div><div><br></div><div>- Enums with <b>availability</b> annotations on some cases. Doug Gregor writes: <i>&quot;<span style="font-size:12.8px">if I have a case that’s only available on macOS 10.12 and newer, it probably shouldn’t show up if I use allValues when running on macOS 10.11.</span>&quot;</i></div><div><br></div><div><div>If we fetch cases from the enum metadata, does this &quot;just work&quot; since the metadata will be coming from whichever version of the library is loaded at runtime? If not, is it at least <i>possible</i> to extract availability info from the metadata? Finally, if not, should we try to synthesize an implementation that uses #available checks, or just refuse to synthesize allCases?</div></div><div><br></div><div>- Should it be possible to add a CaseEnumerable conformance in an <b>extension</b>? My thinking is: we want to make sure the metadata is coming from the module that defines the enum, so we could restrict autogeneration of allCases to that same module. (That is, it wouldn&#39;t be possible to synthesize allCases for a CaseEnumerable extension on an enum from another module.) Although, it may be that I am missing something and this restriction isn&#39;t actually necessary. The question to answer is: in exactly which circumstances can the implementation be synthesized?</div><div><br></div><div><br></div><div>Looking forward to hearing everyone&#39;s thoughts,</div><div><div class="gmail_signature"><div dir="ltr"><div>Jacob<br></div></div></div></div>
</div>