SE-0390: @noncopyable structs and enums

SE-0390 Noncopyable Types Release Notes

In conjunction with the Swift Evolution Review of SE-0390 “Noncopyable Types,” we have a prototype implementation that has been merged into the main branch of Swift on Github and will be available in the next Swift toolchain snapshots behind the -enable-experimental-move-only flag.

There are some known limitations of the current prototype. Some of these are issues we intend to address very soon; others are outside the scope of SE-0390 and will be dealt with as part of future Swift Evolution proposals.

Short-term issues . These are all limitations of the current prototype that we expect to address soon. We hope to fix most of these before the review is complete.

  • Noncopyable types are currently introduced by marking a struct/enum with the attribute @_moveOnly instead of @_noncopyable
  • A property that holds a noncopyable type is not yet fully resilient. If you expose a type containing such a property as part of a framework’s public API, attempts to read the property may attempt to copy the noncopyable type, leading to a runtime crash.
  • A noncopyable type that is generic and has properties whose type depends on the generic type parameter are compiled incorrectly. This means that the compiler /may/ attempt to emit copies of such types and thus result in copied noncopyable type errors emitted by the compiler.
  • You cannot form an Optional containing a noncopyable type. In order to simplify certain use cases, we intend to support Optionals, even though other generic types will not be supported initially (see below). To work around this, one can create non-generic concrete enums that simulate as if one had a generic optional.
  • Struct and enum deinit methods are not yet included in the value witness. This should only be visible to users of the current implementation when capturing a var in an escaping closure and passing the var inout. In such a case, the deinit may not be called.
  • Indirect enums cannot be marked as non copyable and enums cannot have indirect cases with noncopyable payloads.
  • The compiler may crash when compiling with -O,-Osize deinits of noncopyable types that only contain trivial fields.
  • The test toolchain does not yet support autodifferentiation.
  • It is currently possible to switch on a noncopyable enum with deinit or destructure a move only struct with deinit. This will cause the deinit not to run. In future versions this will be banned.
  • The forget keyword has not yet been implemented
  • At -Onone, we shrink lifetimes aggressively towards uses in certain cases rather than shrinking aggressively and then maximizing lifetimes as much as we can like we do with other lexical lifetime values.

Deliberate restrictions . These are limitations that we do not expect to address in the current work, either because they are fundamental to the model or because their implementation requires design work that will need to go through additional Swift Evolution discussion and review.

  • Noncopyable types cannot be used as generic type arguments. For example, you cannot form an Array<N> where N is a noncopyable type. We are exploring approaches and hope to begin a Swift Evolution of this in the near future.
    • As a special case, we do expect to support Optionals containing noncopyable types very soon.
  • Noncopyable types cannot conform to protocols. This is a consequence of the limitation on using noncopyable types as generic type arguments above, and we expect to address it as part of the same effort.
    • As a special case, we allow noncopyable types to be Sendable with the usual Sendable restrictions.
  • Noncopyable types cannot be stored in existentials, including Any, AnyObject, or any Sendable.
  • There are limitations on using noncopyable types in if..let, if..case, switch statements, and for loops. We have pitches for these under discussion [1] and expect to prototype this support soon.

[1] https://forums.swift.org/t/pitch-borrow-and-inout-declaration-keywords/62366

10 Likes