Access control for enum cases

A little bit earlier in the thread, there was a discussion that we potentially don't need a separate notion of a non-exhaustive enum, instead we can define the semantics of something like:

enum E { case e; internal case f(Never) }
// is equivalent to
@nonExhaustive enum E { case e }

where pattern-matching on E values would be exhaustive inside the module (you have access to case f so you can see that f exists but you don't need to match on it due to Never) but non-exhaustive outside it (from the outside, E has some non-public case, so matching is non-exhaustive).

I'm not saying we should necessarily adopt this, but that's one alternative. If we choose that alternative, having a separate mechanism for non-exhaustive non-resilient enums is pure sugar and (arguably?) not necessary.

The other alternative is that impossible cases are ignored outside the module, the same way they are ignored inside the module. This makes the pattern space decomposition consistent across modules (i.e. Space(E) = Space(case e) both inside and outside the module, vs Space(E) = Space(case e) inside and Space(E) = Space(case e) + _ outside), however, this means that if you use an internal case f (no associated values) to emulate @nonExhaustive, you need to use fatalError inside the module. So in such a situation, it would be nice to have a separate @nonExhaustive (or similar mechanism).

Given that there are two alternatives, one of which (arguably?) cleanly describes non-exhaustive enums, my current thinking is to wait for the discussion to settle down for a bit before considering adding another pitch as a dependency.

As you saw, in Cory's pitch Extensible Enumerations for Non-Resilient Libraries, the idea of "version-locked dependencies" came up where potentially the enum could be exhaustive for a set of modules which are "version-locked" and non-exhaustive outside it. I don't want to go too much into the weeds here, but something like that could fit into a more general access control model (strawman syntax):

visible(declaration) = private
visible(file)        = fileprivate
visible(module)      = internal
visible(workspace)   = ??? // workspace = set of version-locked modules
visible              = public

If you want to add @nonExhaustive as a mechanism for governing where the exhaustivity-relation of cases is visible, you'd even be able to select @nonExhaustive(module) vs @nonExhaustive(workspace). [I don't want to derail this thread by discussing a more general access control model further, if you want to discuss it, let's do that over DM or in a new thread. :slight_smile:]

There's also the question about should this be an attribute or should the defaults be changed/warnings be added to remove the language dialect. That's a much more difficult question to answer... I don't have a good sense for what solution the core team would like to see here.

I understand what you're saying about adding non-exhaustive non-resilient enums first. However, I think that if we have some low-hanging fruit -- in terms of getting rough consensus from Swift developers and approval from the core team -- that can improve QoL for people, there's a good argument to be made that we should pick the low-hanging fruit first, instead of tackling the bigger challenge.


Yes. (Thanks for bringing this up, I hadn't explicitly thought about it.)

2 Likes