A Feasible Implementation of Type Disjunctions

For reference, the ability to "unseal" in this way is how it works in Kotlin. This is explained here.

The proposed fix of “just switch on the derived protocol” doesn’t work if any other module can define refinements that the original module cannot have been designed to handle

I might be misunderstanding you, but if my reading of this is correct, you don't switch on the derived protocol, you handle all conformances to the refinement as an existential. In your example, defining public protocol Q: P, which must be defined in P's module (since P is sealed), the switch on only X no longer compiles. You have to update it to this:

public func foo(_ x: any P) {
  switch x {
  case let x as X: print("X")
  case let q as any Q: print("Q")
  }
}

And the ability to exhaustively switch stops there. No one (in any module) can exhaustively switch on an any Q, because it isn't sealed, so when they exhaustively switch on any P, they can drill down to any Q but no further.

So I don't think it's unsound, but that doesn't mean it's a good idea. Like I said, I wouldn't protest too much if a consensus emerges that re-opening shouldn't be allowed. I'm struggling to think of a clear use case for it anyways.

Regarding compiler performance, I believe this feature can and certainly should be done in a way that follows the zero overhead principle, in that it should cost nothing when you don't use it. That's why it's essential that it's opt-in, you only gain this capability when you explicitly request it, and for this it may even be necessary to distinguish "sealing" or "closing" a protocol from disallowing external conformances (I talked about this in this thread).

I imagine it would work by the compiler seeing the sealed and from that enabling the extra work to collect the list of subtypes, and then storing that in the module interface. A concern there might be: if a module decides to use this, does it cost anything for importers of that module who aren't taking advantage of it? Hopefully not as it's just a little extra metadata the compiler can ignore until it sees switches without defaults. Of course importing open source packages can make it a problem for everyone, but that's a general problem SPM needs to figure out (why is it ever compiling package dependencies except once after a version is pulled down, and I mean for my entire dev machine?)

That way, the compiler would only get bogged down chasing an excessively large or complex hierarchy of types if someone decides to mark that hierarchy as sealed, which indicates they want that and might be willing to pay for it... and if it turns out the price tag is too high, they can just turn it back off.

and compile as multi-payload enums as you’ve no doubt surmised

Yes exactly. Knowing all the possibilities means heap allocations can be entirely avoided.

(What if it gets re-opened by a refinement? That would just mean one of the payloads is an existential, but at least you for sure avoid allocations for other cases).