SE-0432: Borrowing and consuming pattern matching for noncopyable types

Hi Swift community,

The review of SE-0432: Borrowing and consuming pattern matching for noncopyable types begins now and runs through 22nd of April, 2023.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager. When emailing the review manager directly, please keep the proposal link at the top of the message.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/main/process.md

Thank you,

Ben Cohen
Review Manager

13 Likes

I feel a little weird that the stored vs. computed distinction is exposed like this. I know the real condition being checked for is "has a read accessor", but since that isn't formally in the language yet this breaks the formal library evolution freedom to change a stored property to a computed one and vice versa. I'd feel better if the baseline mode was determined entirely syntactically. (Though maybe that'll get complicated if we add yielding functions as well…)

Similarly, the addition of borrowing as a limited, alternate form of let-patterns doesn't quite feel justified to me: for non-copyable types, it's implied (when not consuming), and for copyable types, it's something you'd specifically want for performance control (avoiding copies). But if we're talking about performance control, couldn't we use our existing annotations for that? If the input to a switch is a copyable type stored in a borrowing parameter (and perhaps a borrowing local later), then the switch is borrowing by default, not copyable.

(I guess that re-suggests the need for borrowing patterns, though, so that you can disable implicit copies of a matched sub-value within a case of a consuming switch.)


if there is an as T subpattern, and the type of the value being matched is noncopyable, then the switch behavior is consuming. If the value being matched is copyable, there is no effect on the behavior of the switch. This is because some forms of dynamic cast on noncopyable types may require consuming the input value.

This means as T patterns and where clauses can't be mixed. That's not new to this proposal, but it might be worth calling out, or seeing if the consuming-cast mode can be avoided (but it probably can't).

EDIT: Honestly I'd rather as T and var patterns, which are relatively rare, not actually push the switch up to consuming, but rather error out. The less action at a distance and the fewer rules here, the better.

6 Likes

IIRC, it's been exposed since noncopyable types were first introduced, since you can consume the result of a noncopyable computed property without consuming the original value.

As a side note, I think it's somewhat unintuitive that the "standard" computed property accessors that people reach for (get/set) are also incompatible with those of stored properties. If it's possible to change at this point, maybe we should remove those accessors entirely for noncopyable types, in favor of more ownership-explicit ones like borrow/consume/mutate.

I agree. The existing variable binding keywords are very short (and the same length), and other information (like lazy, weak, and the type) is specified elsewhere. If we add borrowing variable declarations in the future, they could be awkward compared to the existing three-letter declaration keywords.

If we pursue the future direction "Automatic borrow deduction for let bindings, and explicitly consuming bindings," maybe it would make sense to be consistent with function parameters and put the ownership in the type annotation. We could then explain that the compiler uses ownership inference when there is no ownership keyword in the annotation, analogous to how the compiler uses type inference when there is no type in the annotation.

switch x {
case .a(let x: borrowing):     // infer the type
    ...
case .b(let x: Int):           // infer the ownership
    ...
case .c(let x):                // infer both
    ...
case .d(let x: borrowing Int): // infer neither
    ...
}
2 Likes

Is this the intended behavior of the BorrowingSwitch upcoming feature flag:

  • The flag will enable the feature in Swift 5 language mode
  • The feature will be enabled by default in Swift 6 language mode

Or will the flag be required to enable the feature in Swift 6 language mode as well?

Thank you.

Apologies, this is a bug in the proposal text.

This is not an upcoming feature: there are no source breaks involved. It is an experimental feature until this feature is accepted (if it is), at which point it won't be behind a flag, because it is source-compatible (except in very edgy edge cases, as described here).

I'll fix the proposal.

2 Likes

Thank you for clarifying.

The more fundamental distinction that's being exposed is between properties that produce borrows and properties that produce owned values. The only "official" way we have to define computed properties right now is by getter/setter, and by its nature, get returns a new value that the caller takes ownership of. We could perhaps hide this notion by artificially limiting switches over a property defined via a getter to be borrows, but the distinction is already exposed in other ways, since you can already pass the result of a get as a consuming parameter but not a stored property value (without also partially consuming the enclosing aggregate). When we get read and modify accessor coroutines as an official feature, then that'll be a mechanism by which you can replace stored properties with computed properties defined using accessor coroutines, or vice versa, in an API- and ABI-compatible way.

3 Likes

Review Conclusion

The proposal has been accepted with modifications.