> On Feb 20, 2017, at 2:40 AM, Florent Bruneau via swift-evolution
> <[email protected]> wrote:
>
> Hi John,
>
> I've spent 3 hours reading the manifesto and its really very interesting.
> Despite the completeness of the included discussion, I have a few
> comments/concerns.
>
>
> The first one is about the use a Copyable protocol whose conformance must be
> explicit in the module of the definition of the type. Since copyability
> shouldn't be modified outside the module that define a type, using a protocol
> seems a bit weird since AFAIK no other protocol has this kind of constraints.
> I do understand this enables some idiomatic constructs (like you example
> where Array conforms to Copyable whenever its elements are copyable), but on
> the other hand this looks a bit confusing to me. I don't have a better
> solution, thought.
This is a very good point. If it were not a protocol it would still be
possible to do things like `Array where Element: Copyable`. We would just need
a new kind of constraint. There is some precedent for that with `class`
constraints.
>
>
>
> Secondly, in your discussion about variable independence, you talk about
> logical dependence: variables that are part of the same container. However,
> whenever this is some concurrency involved, there is also some kind of
> physical dependence. If you have two values stored physically in memory in
> the same cache line, and you may want to modify both independently and
> concurrently (one thread modifies the first value, the second modifies the
> other one), you have some false sharing which impacts the performance since
> the actual writes get sequentialized at CPU level. Is there any plan to take
> this kind of dependencies into account?
>
>
>
> Thirdly, I'm a bit worried about the runtime impact of the dynamic
> enforcement of the law of exclusivity. It seems to me that in many use cases
> we will fall back to the dynamic enforcement because of the language is too
> dynamic to allow static enforcement. As discussed in the manifesto, it's not
> desirable (not doable) to put the full concept of ownership and lifetime in
> the type system. However, I cannot see how interaction between objects will
> work in the current approach. Let suppose I have a concurrent application
> that have a shared ressource that can be used by various workers. Workers can
> be either structures/classes or closures.
>
> ```swift
> struct Ressource {
> /* content is unimportant here */
> }
>
> class Worker {
> shared ressource: Ressource
>
> init(ressource: shared Ressource) {
> self.ressource = ressource
> }
>
> /* ... */
> }
>
> let ressource = Ressource()
>
> for i in 0..<10 {
> let worker = Worker(ressource: ressource)
> worker.schedule()
> }
> waitAllWorkers()
> ```
>
> My understanding of the manifesto is that this kind of construct in not
> really part of it. Is there a way to enforce the workers' lifetime to be
> shorter than the lifetime of the ressource?
>
>
>
> Finally, since I work a lot with imported C code, I'd like to know if there
> is some plan to make the ownership attributes works well with C code. I mean,
> you say that unsafe pointers just force a fall back to not enforcing the law
> of exclusivity, but in my day-to-day work, this is exactly where I need it
> the most: when I have to manipulate a structure that contain a pointer
> allocated by some C code, I cannot allow copy of that structure since that
> pointer may be reallocated and one of the copies would contain a dangling
> pointer. So, is there some way to opt-in for non-copyability of imported code?
>
> I tried, without success, to propose some way to improve pointer-passing
> between C and Swift [1] that could help tracking ownership when interacting
> with C code.
>
>
>
> [1]
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170130/031199.html
>
>
>> Le 17 févr. 2017 à 09:25, John McCall via swift-evolution
>> <[email protected]> a écrit :
>>
>> Hello, swift-evolution.
>>
>> Memory ownership is a topic that keeps poking its head up here. Core team
>> members have mentioned several times that it's something we're interested in
>> working on. Questions sometimes get referred back to it, saying stuff like
>> "we're working on tools to let you control ARC a little better". It's even
>> on the
>> short list of high-priority features for Swift 4:
>>
>> https://github.com/apple/swift-evolution
>>
>> Memory ownership model: an (opt-in) Cyclone/Rust-inspired memory
>> ownership model is highly desired by systems programmers and for other
>> high-performance applications that want predictable and deterministic
>> performance. This feature will fundamentally shape the ABI, from low-level
>> language concerns such as "inout" and low-level "addressors" to its impact
>> on the standard library. While a full memory ownership model is likely too
>> large for Swift 4 stage 1, we need a comprehensive design to understand
>> how it will change the ABI.
>>
>> But that's all pretty vague. What is ownership? What does it mean for
>> programmers? Is somebody ever going to actually write that comprehensive
>> design that was supposed to come out during Stage 1?
>>
>> Well, here you go.
>>
>> I want to emphasize that this document is two things. It is a *manifesto*,
>> because it describes the whole problem and presents a holistic solution to
>> it.
>> And it is a *meta-proposal*, because that holistic solution imagines and a
>> number
>> of changes, each of which is worthy of a proposal; it is, essentially, a
>> proposal
>> that we write a bunch of smaller proposals for each of these changes. But
>> it's
>> not actually a concrete proposal for those smaller changes: it's often
>> lacking in
>> that level of detail, and it leaves a number of questions open. This
>> document, itself,
>> will not undergo the formal evolution process. And it may be that some of
>> these
>> changes aren't really necessary, or that they need major reconsideration
>> before
>> they're appropriate for proposal. But our hope — my hope — is that this
>> document is sufficient for you to understand the holistic approach we're
>> suggesting,
>> or at least puts you in a position where you're comfortable asking questions
>> and trying to figure it out.
>>
>> So, like I said, this isn't a formal proposal. What, then, are we hoping to
>> achieve?
>> Well, we want people to talk about it. We'd like to achieve a consensus
>> about
>> whether this basic approach makes sense for the language and achieves its
>> goals.
>> And, assuming that the consensus is "yes" and that we should go forward with
>> it,
>> we'd like this document — and this thread — to serve as an introduction to
>> the
>> topic that we can refer back to when we're proposing and discussing all of
>> those
>> changes in the weeks to come.
>>
>> With that said, let's get started.
>>
>> John.
>>
>> ------------------------------------------------------------------
>>
>> # Ownership
>>
>> ## Introduction
>>
>> Adding "ownership" to Swift is a major feature with many benefits
>> for programmers. This document is both a "manifesto" and a
>> "meta-proposal" for ownership: it lays out the basic goals of
>> the work, describes a general approach for achieving those goals,
>> and proposes a number of specific changes and features, each of
>> which will need to be separately discussed in a smaller and more
>> targeted proposal. This document is intended to provide a framework
>> for understanding the contributions of each of those changes.
>>
>> ### Problem statement
>>
>> The widespread use of copy-on-write value types in Swift has generally
>> been a success. It does, however, come with some drawbacks:
>>
>> * Reference counting and uniqueness testing do impose some overhead.
>>
>> * Reference counting provides deterministic performance in most cases,
>> but that performance can still be complex to analyze and predict.
>>
>> * The ability to copy a value at any time and thus "escape" it forces
>> the underlying buffers to generally be heap-allocated. Stack allocation
>> is far more efficient but does require some ability to prevent, or at
>> least recognize, attempts to escape the value.
>>
>> Certain kinds of low-level programming require stricter performance
>> guarantees. Often these guarantees are less about absolute performance
>> than *predictable* performance. For example, keeping up with an audio
>> stream is not a taxing job for a modern processor, even with significant
>> per-sample overheads, but any sort of unexpected hiccup is immediately
>> noticeable by users.
>>
>> Another common programming task is to optimize existing code when something
>> about it falls short of a performance target. Often this means finding
>> "hot spots" in execution time or memory use and trying to fix them in some
>> way. When those hot spots are due to implicit copies, Swift's current
>> tools for fixing the problem are relatively poor; for example, a programmer
>> can fall back on using unsafe pointers, but this loses a lot of the
>> safety benefits and expressivity advantages of the library collection types.
>>
>> We believe that these problems can be addressed with an opt-in set of
>> features that we collectively call *ownership*.
>>
>> ### What is ownership?
>>
>> *Ownership* is the responsibility of some piece of code to
>> eventually cause a value to be destroyed. An *ownership system*
>> is a set of rules or conventions for managing and transferring
>> ownership.
>>
>> Any language with a concept of destruction has a concept of
>> ownership. In some languages, like C and non-ARC Objective-C,
>> ownership is managed explicitly by programmers. In other
>> languages, like C++ (in part), ownership is managed by the
>> language. Even languages with implicit memory management still
>> have libraries with concepts of ownership, because there are
>> other program resources besides memory, and it is important
>> to understand what code has the responsibility to release
>> those resources.
>>
>> Swift already has an ownership system, but it's "under the covers":
>> it's an implementation detail that programmers have little
>> ability to influence. What we are proposing here is easy
>> to summarize:
>>
>> - We should add a core rule to the ownership system, called
>> the Law of Exclusivity, which requires the implementation
>> to prevent variables from being simultaneously accessed
>> in conflicting ways. (For example, being passed `inout`
>> to two different functions.) This will not be an opt-in
>> change, but we believe that most programs will not be
>> adversely affected.
>>
>> - We should add features to give programmers more control over
>> the ownership system, chiefly by allowing the propagation
>> of "shared" values. This will be an opt-in change; it
>> will largely consist of annotations and language features
>> which programmers can simply not use.
>>
>> - We should add features to allow programmers to express
>> types with unique ownership, which is to say, types that
>> cannot be implicitly copied. This will be an opt-in
>> feature intended for experienced programmers who desire
>> this level of control; we do not intend for ordinary
>> Swift programming to require working with such types.
>>
>> These three tentpoles together have the effect of raising
>> the ownership system from an implementation detail to a more
>> visible aspect of the language. They are also somewhat
>> inseparable, for reasons we'll explain, although of course they
>> can be prioritized differently. For these reasons, we will
>> talk about them as a cohensive feature called "ownership".
>>
>> ### A bit more detail
>>
>> The basic problem with Swift's current ownership system
>> is copies, and all three tentpoles of ownership are about
>> avoiding copies.
>>
>> A value may be used in many different places in a program.
>> The implementation has to ensure that some copy of the value
>> survives and is usable at each of these places. As long as
>> a type is copyable, it's always possible to satisfy that by
>> making more copies of the value. However, most uses don't
>> actually require ownership of their own copy. Some do: a
>> variable that didn't own its current value would only be
>> able to store values that were known to be owned by something
>> else, which isn't very useful in general. But a simple thing
>> like reading a value out of a class instance only requires that
>> the instance still be valid, not that the code doing the
>> read actually own a reference to it itself. Sometimes the
>> difference is obvious, but often it's impossible to know.
>> For example, the compiler generally doesn't know how an
>> arbitrary function will use its arguments; it just falls
>> back on a default rule for whether to pass ownership of
>> the value. When that default rule is wrong, the program
>> will end up making extra copies at runtime. So one simple
>> thing we can do is allow programs to be more explicit at
>> certain points about whether they need ownership or not.
>>
>> That closely dovetails with the desire to support non-copyable
>> types. Most resources require a unique point of destruction:
>> a memory allocation can only be freed once, a file can only
>> be closed once, a lock can only be released once, and so on.
>> An owning reference to such a resource is therefore naturally
>> unique and thus non-copyable. Of course, we can artificially
>> allow ownership to be shared by, say, adding a reference count
>> and only destroying the resource when the count reaches zero.
>> But this can substantially increase the overhead of working
>> with the resource; and worse, it introduces problems with
>> concurrency and re-entrancy. If ownership is unique, and the
>> language can enforce that certain operations on a resource
>> can only be performed by the code that owns the resource,
>> then by construction only one piece of code can perform those
>> operations at a time. As soon as ownership is shareable,
>> that property disappears. So it is interesting for the
>> language to directly support non-copyable types because they
>> allow the expression of optimally-efficient abstractions
>> over resources. However, working with such types requires
>> all of the abstraction points like function arguments to
>> be correctly annotated about whether they transfer ownership,
>> because the compiler can no longer just make things work
>> behind the scenes by adding copies.
>>
>> Solving either of these problems well will require us to
>> also solve the problem of non-exclusive access to variables.
>> Swift today allows nested accesses to the same variable;
>> for example, a single variable can be passed as two different
>> `inout` arguments, or a method can be passed a callback that
>> somehow accesses the same variable that the method was called on.
>> Doing this is mostly discouraged, but it's not forbidden,
>> and both the compiler and the standard library have to bend
>> over backwards to ensure that the program won't misbehave
>> too badly if it happens. For example, `Array` has to retain
>> its buffer during an in-place element modification; otherwise,
>> if that modification somehow reassigned the array variable,
>> the buffer would be freed while the element was still being
>> changed. Similarly, the compiler generally finds it difficult
>> to prove that values in memory are the same at different points
>> in a function, because it has to assume that any opaque
>> function call might rewrite all memory; as a result, it
>> often has to insert copies or preserve redundant loads
>> out of paranoia. Worse, non-exclusive access greatly
>> limits the usefulness of explicit annotations. For example,
>> a "shared" argument is only useful if it's really guaranteed
>> to stay valid for the entire call, but the only way to
>> reliably satisfy that for the current value of a variable
>> that can be re-entrantly modified is to make a copy and pass
>> that instead. It also makes certain important patterns
>> impossible, like stealing the current value of a variable
>> in order to build something new; this is unsound if the
>> variable can be accessed by other code in the middle.
>> The only solution to this is to establish a rule that prevents
>> multiple contexts from accessing the same variable at the
>> same time. This is what we propose to do with the Law
>> of Exclusivity.
>>
>> All three of these goals are closely linked and mutually
>> reinforcing. The Law of Exclusivity allows explicit annotations
>> to actually optimize code by default and enables mandatory
>> idioms for non-copyable types. Explicit annotations create
>> more optimization opportunities under the Law and enable
>> non-copyable types to function. Non-copyable types validate
>> that annotations are optimal even for copyable types and
>> create more situations where the Law can be satisfied statically.
>>
>> ### Criteria for success
>>
>> As discussed above, it is the core team's expectation that
>> ownership can be delivered as an opt-in enhancement to Swift.
>> Programmers should be able to largely ignore ownership and not
>> suffer for it. If this expectation proves to not be satisfiable,
>> we will reject ownership rather than imposing substantial
>> burdens on regular programs.
>>
>> The Law of Exclusivity will impose some new static and dynamic
>> restrictions. It is our belief that these restrictions will only
>> affect a small amount of code, and only code that does things
>> that we already document as producing unspecified results.
>> These restrictions, when enforced dynamically, will also hurt
>> performance. It is our hope that this will be "paid for" by
>> the improved optimization potential. We will also provide tools
>> for programmers to eliminate these safety checks where necessary.
>> We will discuss these restrictions in greater detail later in this
>> document.
>>
>> ## Core definitions
>>
>> ### Values
>>
>> Any discussion of ownership systems is bound to be at a lower
>> level of abstraction. We will be talking a lot about
>> implementation topics. In this context, when we say "value",
>> we mean a specific instance of a semantic, user-language value.
>>
>> For example, consider the following Swift code:
>>
>> ```
>> var x = [1,2,3]
>> var y = x
>> ```
>>
>> People would often say that `x` and `y` have the same value
>> at this point. Let's call this a *semantic value*. But at
>> the level of the implementation, because the variables `x` and `y`
>> can be independently modified, the value in `y` must be a
>> copy of the value in `x`. Let's call this a *value instance*.
>> A value instance can be moved around in memory and remain the
>> same value instance, but a copy always yields a new value instance.
>> For the remainder of this document, when we use "value" without any
>> qualification, we mean it in this low-level sense of value instance.
>>
>> What it means to copy or destroy a value instance depends on the type:
>>
>> * Some types do not require extra work besides copying their
>> byte-representation; we call these *trivial*. For example,
>> `Int` and `Float` are trivial types, as are ordinary `struct`s
>> and `enum`s containing only such values. Most of what we have
>> to say about ownership in this document doesn't apply to the
>> values of such types. However, the Law of Exclusivity will still
>> apply to them.
>>
>> * For reference types, the value instance is a reference to an object.
>> Copying the value instance means making a new reference, which
>> increases the reference count. Destroying the value instance means
>> destroying a reference, which decreases the reference count. Decreasing
>> the reference count can, of course, drop it to zero and thus destroy
>> the object, but it's important to remember that all this talk about
>> copying and destroying values means manipulating reference counts,
>> not copying the object or (necessarily) destroying it.
>>
>> * For copy-on-write types, the value instance includes a reference to
>> a buffer, which then works basically like a reference type. Again,
>> it is important to remember that copying the value doesn't mean
>> copying the contents of the buffer into a new buffer.
>>
>> There are similar rules for every kind of type.
>>
>> ### Memory
>>
>> In general, a value can be owned in one of two ways: it can be
>> "in flight", a temporary value owned by a specific execution context
>> which computed the value as an operand, or it can be "at rest",
>> stored in some sort of memory.
>>
>> We don't need to focus much on temporary values because their
>> ownership rules are straightforward. Temporary values are created
>> as the result of some expression; that expression is used in some
>> specific place; the value is needed in that place and not
>> thereafter; so the implementation should clearly take all possible
>> steps to forward the value directly to that place instead of
>> forcing it to be copied. Users already expect all of this to
>> happen, and there's nothing really to improve here.
>>
>> Therefore, most of our discussion of ownership will center around
>> values stored in memory. There are five closely related concepts
>> in Swift's treatment of memory.
>>
>> A *storage declaration* is the language-syntax concept of a declaration
>> that can be treated in the language like memory. Currently, these are
>> always introduced with `let`, `var`, and `subscript`. A storage
>> declaration has a type. It also has an implementation which defines
>> what it means to read or write the storage. The default implementation
>> of a `var` or `let` just creates a new variable to store the value,
>> but storage declarations can also be computed, and so there needn't
>> be any variables at all behind one.
>>
>> A *storage reference expression* is the syntax concept of an expression
>> that refers to storage. This is similar to the concept from other
>> languages of an "l-value", except that it isn't necessarily usable on
>> the left side of an assignment because the storage doesn't have to be
>> mutable.
>>
>> A *storage reference* is the language-semantics concept of a fully
>> filled-in reference to a specific storage declaration. In other
>> words, it is the result of evaluating a storage reference expression
>> in the abstract, without actually accessing the storage. If the
>> storage is a member, this includes a value or storage reference
>> for the base. If the storage is a subscript, this includes a value
>> for the index. For example, a storage reference expression like
>> `widgets[i].weight` might abstractly evaluate to this storage reference:
>>
>> * the storage for the property `var weight: Double` of
>> * the storage for the subscript `subscript(index: Int)` at index value `19:
>> Int` of
>> * the storage for the local variable `var widgets: [Widget]`
>>
>> A *variable* is the semantics concept of a unique place in
>> memory that stores a value. It's not necessarily mutable, at least
>> as we're using it in this document. Variables are usually created for
>> storage declarations, but they can also be created dynamically in
>> raw memory, e.g. using UnsafeRawPointer. A variable always has a
>> specific type. It also has a *lifetime*, i.e. a point in the language
>> semantics where it comes into existence and a point (or several)
>> where it is destroyed.
>>
>> A *memory location* is a contiguous range of addressable memory. In
>> Swift, this is mostly an implementation concept. Swift does not
>> guarantee that any particular variable will have a consistent memory
>> location throughout its lifetime, or in fact be stored in a memory
>> location at all. But a variable can sometimes be temporarily forced
>> to exist at a specific, consistent location: e.g. it can be passed
>> `inout` to `withUnsafeMutablePointer`.
>>
>> ### Accesses
>>
>> A particular evaluation of a storage reference expression is
>> called an access. Accesses come in three kinds: *reads*,
>> *assignments*, and *modifications*. Assignments and modifications
>> are both *writes*, with the difference being that an assignment
>> completely replaces the old value without reading it, while a
>> modification does rely on the old value.
>>
>> All storage reference expressions are classified into one of these
>> three kinds of access based on the context in which the expression
>> appears. It is important to note that this decision is superficial:
>> it relies only on the semantic rules of the immediate context, not
>> on a deeper analysis of the program or its dynamic behavior.
>> For example, a storage reference passed as an `inout` argument
>> is always evaluated as a modification in the caller, regardless
>> of whether the callee actually uses the current value, performs
>> any writes to it, or even refers to it at all.
>>
>> The evaluation of a storage reference expression is divided into
>> two phases: it is first formally evaluated to a storage reference,
>> and then a formal access to that storage reference occurs for some
>> duration. The two phases are often evaluated in immediate
>> succession, but they can be separated in complex cases, such as
>> when an `inout` argument is not the last argument to a call.
>> The purpose of this phase division is to minimize the duration of
>> the formal access while still preserving, to the greatest extent
>> possible, Swift's left-to-right evaluation rules.
>>
>> ## The Law of Exclusivity
>>
>> With all of that established, we can succinctly state the first
>> part of this proposal, the Law of Exclusivity:
>>
>>> If a storage reference expression evaluates to a storage
>>> reference that is implemented by a variable, then the formal
>>> access duration of that access may not overlap the formal
>>> access duration of any other access to the same variable
>>> unless both accesses are reads.
>>
>> This is intentionally vague: it merely says that accesses
>> "may not" overlap, without specifying how that will be
>> enforced. This is because we will use different enforcement
>> mechanisms for different kinds of storage. We will discuss
>> those mechanisms in the next major section. First, however,
>> we need to talk in general about some of the implications of
>> this rule and our approach to satisfying it.
>>
>> ### Duration of exclusivity
>>
>> The Law says that accesses must be exclusive for their entire
>> formal access duration. This duration is determined by the
>> immediate context which causes the access; that is, it's a
>> *static* property of the program, whereas the safety problems
>> we laid out in the introduction are *dynamic*. It is a general
>> truth that static approaches to dynamic problems can only be
>> conservatively correct: there will be dynamically-reasonable
>> programs that are nonetheless rejected. It is fair to ask how
>> that general principle applies here.
>>
>> For example, when storage is passed as an `inout` argument, the
>> access lasts for the duration of the call. This demands
>> caller-side enforcement that no other accesses can occur to
>> that storage during the call. Is it possible that this is too
>> coarse-grained? After all, there may be many points within the
>> called function where it isn't obviously using its `inout`
>> argument. Perhaps we should track accesses to `inout` arguments
>> at a finer-grained level, within the callee, instead of attempting
>> to enforce the Law of Exclusivity in the caller. The problem
>> is that that idea is simply too dynamic to be efficiently
>> implemented.
>>
>> A caller-side rule for `inout` has one key advantage: the
>> caller has an enormous amount of information about what
>> storage is being passed. This means that a caller-side rule
>> can often be enforced purely statically, without adding dynamic
>> checks or making paranoid assumptions. For example, suppose
>> that a function calls a `mutating` method on a local variable.
>> (Recall that `mutating` methods are passed `self` as an `inout`
>> argument.) Unless the variable has been captured in an
>> escaping closure, the function can easily examine every
>> access to the variable to see that none of them overlap
>> the call, thus proving that the rule is satisfied. Moreover,
>> that guarantee is then passed down to the callee, which can
>> use that information to prove the safety of its own accesses.
>>
>> In contrast, a callee-side rule for `inout` cannot take
>> advantage of that kind of information: the information is
>> simply discarded at the point of the call. This leads to the
>> widespread optimization problems that we see today, as
>> discussed in the introduction. For example, suppose that
>> the callee loads a value from its argument, then calls
>> a function which the optimizer cannot reason about:
>>
>> ```
>> extension Array {
>> mutating func organize(_ predicate: (Element) -> Bool) {
>> let first = self[0]
>> if !predicate(first) { return }
>> ...
>> // something here uses first
>> }
>> }
>> ```
>>
>> Under a callee-side rule, the optimizer must copy `self[0]`
>> into `first` because it must assume (paranoidly) that
>> `predicate` might somehow turn around and modify the
>> variable that `self` was bound to. Under a caller-side
>> rule, the optimizer can use the copy of value held in the
>> array element for as long as it can continue to prove that
>> the array hasn't been modified.
>>
>> Moreover, as the example above suggests, what sort of code
>> would we actually be enabling by embracing a callee-side rule?
>> A higher-order operation like this should not have to worry
>> about the caller passing in a predicate that re-entrantly
>> modifies the array. Simple implementation choices, like
>> making the local variable `first` instead of re-accessing
>> `self[0]` in the example above, would become semantically
>> important; maintaining any sort of invariant would be almost
>> inconceivable. It is no surprise that Swift's libraries
>> generally forbid this kind of re-entrant access. But,
>> since the library can't completely prevent programmers
>> from doing it, the implementation must nonetheless do extra
>> work at runtime to prevent such code from running into
>> undefined behavior and corrupting the process. Because it
>> exists solely to work around the possibility of code that
>> should never occur in a well-written program, we see this
>> as no real loss.
>>
>> Therefore, this proposal generally proposes access-duration
>> rules like caller-side `inout` enforcement, which allow
>> substantial optimization opportunities at little semantic
>> cost.
>>
>> ### Components of value and reference types
>>
>> We've been talking about *variables* a lot. A reader might
>> reasonably wonder what all this means for *properties*.
>>
>> Under the definition we laid out above, a property is a
>> storage declaration, and a stored property creates a
>> corresponding variable in its container. Accesses to that
>> variable obviously need to obey the Law of Exclusivity, but are
>> there any additional restrictions in play due to the fact
>> that the properties are organized together into a container?
>> In particular, should the Law of Exclusivity prevent accesses
>> to different properties of the same variable or value from
>> overlapping?
>>
>> Properties can be classified into three groups:
>> - instance properties of value types,
>> - instance properties of reference types, and
>> - `static` and `class` properties on any kind of type.
>>
>> We propose to always treat reference-type and `static` properties
>> as independent from one another other, but to treat value-type
>> properties as generally non-independent outside of a specific
>> (but important) special case. That's a potentially significant
>> restriction, and it's reasonable to wonder both why it's necessary
>> and why we need to draw this distinction between different
>> kinds of property. There are three reasons.
>>
>> #### Independence and containers
>>
>> The first relates to the container.
>>
>> For value types, it is possible to access both an individual
>> property and the entire aggregate value. It is clear that an
>> access to a property can conflict with an access to the aggregate,
>> because an access to the aggregate is essentially an access to
>> all of the properties at once. For example, consider a variable
>> (not necessarily a local one) `p: Point` with stored properties
>> `x`, `y`, and `z`. If it were possible to simultaneously and
>> independently modify `p` and `p.x`, that would be an enormous
>> hole in the Law of Exclusivity. So we do need to enforce the
>> Law somehow here. We have three options.
>>
>> (This may make more sense after reading the main section
>> about enforcing the Law.)
>>
>> The first option is to simply treat `p.x` as also an access to `p`.
>> This neatly eliminates the hole because whatever enforcement
>> we're using for `p` will naturally prevent conflicting accesses
>> to it. But this will also prevent accesses to different
>> properties from overlapping, because each will cause an access
>> to `p`, triggering the enforcement.
>>
>> The other two options involve reversing that relationship.
>> We could split enforcement out for all the individual stored
>> properties, not for the aggregate: an access to `p` would be
>> treated as an access to `p.x`, `p.y`, and `p.z`. Or we could
>> parameterize enforcement and teach it to record the specific
>> path of properties being accessed: "", ".x", and so on.
>> Unfortunately, there are two problems with these schemes.
>> The first is that we don't always know the full set of
>> properties, or which properties are stored; the implementation
>> of a type might be opaque to us due to e.g. generics or
>> resilience. An access to a computed property must be treated
>> as an access to the whole value because it involves passing
>> the variable to a getter or setter either `inout` or `shared`;
>> thus it does actually conflict with all other properties.
>> Attempting to make things work despite that by using dynamic
>> information would introduce ubiquitous bookkeeping into
>> value-type accessors, endangering the core design goal of
>> value types that they serve as a low-cost abstraction tool.
>> The second is that, while these schemes can be applied to
>> static enforcement relatively easily, applying them to
>> dynamic enforcement would require a fiendish amount of
>> bookkeeping to be carried out dynamically; this is simply
>> not compatible with our performance goals.
>>
>> Thus, while there is a scheme which allows independent access
>> to different properties of the same aggregate value, it
>> requires us to be using static enforcement for the aggregate
>> access and to know that both properties are stored. This is an
>> important special case, but it is just a special case.
>> In all other cases, we must fall back on the general rule
>> that an access to a property is also an access to the aggregate.
>>
>> These considerations do not apply to `static` properties and
>> properties of reference types. There are no language constructs
>> in Swift which access every property of a class simultaneously,
>> and it doesn't even make sense to talk about "every" `static`
>> property of a type because an arbitrary module can add a new
>> one at any time.
>>
>> #### Idioms of independent access
>>
>> The second relates to user expectations.
>>
>> Preventing overlapping accesses to different properties of a
>> value type is at most a minor inconvenience. The Law of Exclusivity
>> prevents "spooky action at a distance" with value types anyway:
>> for example, calling a method on a variable cannot kick off a
>> non-obvious sequence of events which eventually reach back and
>> modify the original variable, because that would involve two
>> conflicting and overlapping accesses to the same variable.
>>
>> In contrast, many established patterns with reference types
>> depend on exactly that kind of notification-based update. In
>> fact, it's not uncommon in UI code for different properties of
>> the same object to be modified concurrently: one by the UI and
>> the other by some background operation. Preventing independent
>> access would break those idioms, which is not acceptable.
>>
>> As for `static` properties, programmers expect them to be
>> independent global variables; it would make no sense for
>> an access to one global to prevent access to another.
>>
>> #### Independence and the optimizer
>>
>> The third relates to the optimization potential of properties.
>>
>> Part of the purpose of the Law of Exclusivity is that it
>> allows a large class of optimizations on values. For example,
>> a non-`mutating` method on a value type can assume that `self`
>> remains exactly the same for the duration of the method. It
>> does not have to worry that an unknown function it calls
>> in the middle will somehow reach back and modify `self`,
>> because that modification would violate the Law. Even in a
>> `mutating` method, no code can access `self` unless the
>> method knows about it. Those assumptions are extremely
>> important for optimizing Swift code.
>>
>> However, these assumptions simply cannot be done in general
>> for the contents of global variables or reference-type
>> properties. Class references can be shared arbitrarily,
>> and the optimizer must assume that an unknown function
>> might have access to the same instance. And any code in
>> the system can potentially access a global variable (ignoring
>> access control). So the language implementation would
>> gain little to nothing from treating accesses to different
>> properties as non-independent.
>>
>> #### Subscripts
>>
>> Much of this discussion also applies to subscripts, though
>> in the language today subscripts are never technically stored.
>> Accessing a component of a value type through a subscript
>> is treated as accessing the entire value, and so is considered
>> to overlap any other access to the value. The most important
>> consequence of this is that two different array elements cannot
>> be simultaneously accessed. This will interfere with certain
>> common idioms for working with arrays, although some cases
>> (like concurrently modifying different slices of an array)
>> are already quite problematic in Swift. We believe that we
>> can mitigate the majority of the impact here with targetted
>> improvements to the collection APIs.
>>
>> ## Enforcing the Law of Exclusivity
>>
>> There are three available mechanisms for enforcing the Law of
>> Exclusivity: static, dynamic, and undefined. The choice
>> of mechanism must be decidable by a simple inspection of the
>> storage declaration, because the definition and all of its
>> direct accessors must agree on how it is done. Generally,
>> it will be decided by the kind of storage being declared,
>> its container (if any), and any attributes that might be
>> present.
>>
>> ### Static enforcement
>>
>> Under static enforcement, the compiler detects that the
>> law is being violated and reports an error. This is the
>> preferred mechanism where possible because it is safe, reliable,
>> and imposes no runtime costs.
>>
>> This mechanism can only be used when it is perfectly decidable.
>> For example, it can be used for value-type properties because
>> the Law, recursively applied, ensures that the base storage is
>> being exclusively accessed. It cannot be used for ordinary
>> reference-type properties because there is no way to prove in
>> general that a particular object reference is the unique
>> reference to an object. However, if we supported
>> uniquely-referenced class types, it could be used for their
>> properties.
>>
>> In some cases, where desired, the compiler may be able to
>> preserve source compatibility and avoid an error by implicitly
>> inserting a copy instead. This likely something we would only
>> do in a source-compatibility mode.
>>
>> Static enforcement will be used for:
>>
>> - immutable variables of all kinds,
>>
>> - local variables, except as affected by the use of closures
>> (see below),
>>
>> - `inout` arguments, and
>>
>> - instance properties of value types.
>>
>> ### Dynamic enforcement
>>
>> Under dynamic enforcement, the implementation will maintain
>> a record of whether each variable is currently being accessed.
>> If a conflict is detected, it will trigger a dynamic failure.
>> The compiler may emit an error statically if it detects that
>> dynamic enforcement will always detect a conflict.
>>
>> The bookkeeping requires two bits per variable, using a tri-state
>> of "unaccessed", "read", and "modified". Although multiple
>> simultaneous reads can be active at once, a full count can be
>> avoided by saving the old state across the access, with a little
>> cleverness.
>>
>> The bookkeeping is intended to be best-effort. It should reliably
>> detect deterministic violations. It is not required to detect
>> race conditions; it often will, and that's good, but it's not
>> required to. It *is* required to successfully handle the case
>> of concurrent reads without e.g. leaving the bookkeeping record
>> permanently in the "read" state. But it is acceptable for the
>> concurrent-reads case to e.g. leave the bookkeeping record in
>> the "unaccessed" case even if there are still active readers;
>> this permits the bookkeeping to use non-atomic operations.
>> However, atomic operations would have to be used if the bookkeeping
>> records were packed into a single byte for, say, different
>> properties of a class, because concurrent accesses are
>> allowed on different variables within a class.
>>
>> When the compiler detects that an access is "instantaneous",
>> in the sense that none of the code executed during the access
>> can possibly cause a re-entrant access to the same variable,
>> it can avoid updating the bookkeeping record and instead just
>> check that it has an appropriate value. This is common for
>> reads, which will often simply copy the value during the access.
>> When the compiler detects that all possible accesses are
>> instantaneous, e.g. if the variable is `private` or `internal`,
>> it can eliminate all bookkeeping. We expect this to be fairly
>> common.
>>
>> Dynamic enforcement will be used for:
>>
>> - local variables, when necessary due to the use of closures
>> (see below),
>>
>> - instance properties of class types,
>>
>> - `static` and `class` properties, and
>>
>> - global variables.
>>
>> We should provide an attribute to allow dynamic enforcement
>> to be downgraded to undefined enforcement for a specific
>> property or class. It is likely that some clients will find
>> the performance consequences of dynamic enforcement to be
>> excessive, and it will be important to provide them an opt-out.
>> This will be especially true in the early days of the feature,
>> while we're still exploring implementation alternatives and
>> haven't yet implemented any holistic optimizations.
>>
>> Future work on isolating class instances may allow us to
>> use static enforcement for some class instance properties.
>>
>> ### Undefined enforcement
>>
>> Undefined enforcement means that conflicts are not detected
>> either statically or dynamically, and instead simply have
>> undefined behavior. This is not a desireable mechanism
>> for ordinary code given Swift's "safe by default" design,
>> but it's the only real choice for things like unsafe pointers.
>>
>> Undefined enforcement will be used for:
>>
>> - the `memory` properties of unsafe pointers.
>>
>> ### Enforcement for local variables captured by closures
>>
>> Our ability to statically enforce the Law of Exclusivity
>> relies on our ability to statically reason about where
>> uses occur. This analysis is usually straightforward for a
>> local variable, but it becomes complex when the variable is
>> captured in a closure because the control flow leading to
>> the use can be obscured. A closure can potentially be
>> executed re-entrantly or concurrently, even if it's known
>> not to escape. The following principles apply:
>>
>> - If a closure `C` potentially escapes, then for any variable
>> `V` captured by `C`, all accesses to `V` potentially executed
>> after a potential escape (including the accesses within `C`
>> itself) must use dynamic enforcement unless all such acesses
>> are reads.
>>
>> - If a closure `C` does not escape a function, then its
>> use sites within the function are known; at each, the closure
>> is either directly called or used as an argument to another
>> call. Consider the set of non-escaping closures used at
>> each such call. For each variable `V` captured by `C`, if
>> any of those closures contains a write to `V`, all accesses
>> within those closures must use dynamic enforcement, and
>> the call is treated for purposes of static enforcement
>> as if it were a write to `V`; otherwise, the accesses may use
>> static enforcement, and the call is treated as if it were a
>> read of `V`.
>>
>> It is likely that these rules can be improved upon over time.
>> For example, we should be able to improve on the rule for
>> direct calls to closures.
>>
>> ## Explicit tools for ownership
>>
>> ### Shared values
>>
>> A lot of the discussion in this section involves the new concept
>> of a *shared value*. As the name suggests, a shared value is a
>> value that has been shared with the current context by another
>> part of the program that owns it. To be consistent with the
>> Law of Exclusivity, because multiple parts of the program can
>> use the value at once, it must be read-only in all of them
>> (even the owning context). This concept allows programs to
>> abstract over values without copying them, just like `inout`
>> allows programs to abstract over variables.
>>
>> (Readers familiar with Rust will see many similarities between
>> shared values and Rust's concept of an immutable borrow.)
>>
>> When the source of a shared value is a storage reference, the
>> shared value acts essentially like an immutable reference
>> to that storage. The storage is accessed as a read for the
>> duration of the shared value, so the Law of Exclusivity
>> guarantees that no other accesses will be able to modify the
>> original variable during the access. Some kinds of shared
>> value may also bind to temporary values (i.e. an r-value).
>> Since temporary values are always owned by the current
>> execution context and used in one place, this poses no
>> additional semantic concerns.
>>
>> A shared value can be used in the scope that binds it
>> just like an ordinary parameter or `let` binding.
>> If the shared value is used in a place that requires
>> ownership, Swift will simply implicitly copy the value —
>> again, just like an ordinary parameter or `let` binding.
>>
>> #### Limitations of shared values
>>
>> This section of the document describes several ways to form
>> and use shared values. However, our current design does not
>> provide general, "first-class" mechanisms for working with them.
>> A program cannot return a shared value, construct an array
>> of shared values, store shared values into `struct` fields,
>> and so on. These limitations are similar to the existing
>> limitations on `inout` references. In fact, the similarities
>> are so common that it will be useful to have a term that
>> encompasses both: we will call them *ephemerals*.
>>
>> The fact that our design does not attempt to provide first-class
>> facilities for ephemerals is a well-considered decision,
>> born from a trio of concerns:
>>
>> - We have to scope this proposal to something that can
>> conceivably be implemented in the coming months. We expect this
>> proposal to yield major benefits to the language and its
>> implementaton, but it is already quite broad and aggressive.
>> First-class ephemerals would add enough complexity to the
>> implementation and design that they are clearly out of scope.
>> Furthermore, the remaining language-design questions are
>> quite large; several existing languages have experimented
>> with first-class ephemerals, and the results haven't been
>> totally satisfactory.
>>
>> - Type systems trade complexity for expressivity.
>> You can always accept more programs by making the type
>> system more sophisticated, but that's not always a good
>> trade-off. The lifetime-qualification systems behind
>> first-class references in languages like Rust add a lot
>> of complexity to the user model. That complexity has real
>> costs for users. And it's still inevitably necessary
>> to sometimes drop down to unsafe code to work around the
>> limitations of the ownership system. Given that a line
>> does have to drawn somewhere, it's not completely settled
>> that lifetime-qualification systems deserve to be on the
>> Swift side of the line.
>>
>> - A Rust-like lifetime system would not necessarily be
>> as powerful in Swift as it is in Rust. Swift intentionally
>> provides a language model which reserves a lot of
>> implementation flexibility to both the authors of types
>> and to the Swift compiler itself.
>>
>> For example, polymorphic storage is quite a bit more
>> flexible in Swift than it is in Rust. A
>> `MutableCollection` in Swift is required to implement a
>> `subscript` that provides accessor to an element for an index,
>> but the implementation can satisfy this pretty much any way
>> it wants. If generic code accesses this `subscript`, and it
>> happens to be implemented in a way that provides direct access
>> to the underlying memory, then the access will happen in-place;
>> but if the `subscript` is implemented with a computed getter
>> and setter, then the access will happen in a temporary
>> variable and the getter and setter will be called as necessary.
>> This only works because Swift's access model is highly
>> lexical and maintains the ability to run arbitrary code
>> at the end of an access. Imagine what it would take to
>> implement a loop that added these temporary mutable
>> references to an array — each iteration of the loop would
>> have to be able to queue up arbitrary code to run as a clean-up
>> when the function was finished with the array. This would
>> hardly be a low-cost abstraction! A more Rust-like
>> `MutableCollection` interface that worked within the
>> lifetime rules would have to promise that the `subscript`
>> returned a pointer to existing memory; and that wouldn't
>> allow a computed implementation at all.
>>
>> A similar problem arises even with simple `struct` members.
>> The Rust lifetime rules say that, if you have a pointer to
>> a `struct`, you can make a pointer to a field of that `struct`
>> and it'll have the same lifetime as the original pointer.
>> But this assumes not only that the field is actually stored
>> in memory, but that it is stored *simply*, such that you can
>> form a simple pointer to it and that pointer will obey the
>> standard ABI for pointers to that type. This means that
>> Rust cannot use layout optimizations like packing boolean
>> fields together in a byte or even just decreasing the
>> alignment of a field. This is not a guarantee that we are
>> willing to make in Swift.
>>
>> For all of these reasons, while we remain theoretically
>> interested in exploring the possibilities of a more
>> sophisticated system that would allow broader uses of
>> ephemerals, we are not proposing to take that on now. Since
>> such a system would primarily consist of changes to the type
>> system, we are not concerned that this will cause ABI-stability
>> problems in the long term. Nor are we concerned that we will
>> suffer from source incompatibilities; we believe that any
>> enhancements here can be done as extensions and generalizations
>> of the proposed features.
>>
>> ### Local ephemeral bindings
>>
>> It is already a somewhat silly limitation that Swift provides
>> no way to abstract over storage besides passing it as an
>> `inout` argument. It's an easy limitation to work around,
>> since programmers who want a local `inout` binding can simply
>> introduce a closure and immediately call it, but that's an
>> awkward way of achieving something that ought to be fairly easy.
>>
>> Shared values make this limitation even more apparent, because
>> a local shared value is an interesting alternative to a local `let`:
>> it avoids a copy at the cost of preventing other accesses to
>> the original storage. We would not encourage programmers to
>> use `shared` instead of `let` throughout their code, especially
>> because the optimizer will often be able to eliminate the copy
>> anyway. However, the optimizer cannot always remove the copy,
>> and so the `shared` micro-optimization can be useful in select
>> cases. Furthermore, eliminating the formal copy may also be
>> semantically necessary when working with non-copyable types.
>>
>> We propose to remove this limitation in a straightforward way:
>>
>> ```
>> inout root = &tree.root
>>
>> shared elements = self.queue
>> ```
>>
>> The initializer is required and must be a storage reference
>> expression. The access lasts for the remainder of the scope.
>>
>> ### Function parameters
>>
>> Function parameters are the most important way in which
>> programs abstract over values. Swift currently provides
>> three kinds of argument-passing:
>>
>> - Pass-by-value, owned. This is the rule for ordinary
>> arguments. There is no way to spell this explicitly.
>>
>> - Pass-by-value, shared. This is the rule for the `self`
>> arguments of `nonmutating` methods. There is no way to
>> spell this explicitly.
>>
>> - Pass-by-reference. This is the rule used for `inout`
>> arguments and the `self` arguments of `mutating` methods.
>>
>> Our proposal here is just to allow the non-standard
>> cases to be spelled explicitly:
>>
>> - A function argument can be explicitly declared `owned`:
>>
>> ```
>> func append(_ values: owned [Element]) {
>> ...
>> }
>> ```
>>
>> This cannot be combined with `shared` or `inout`.
>>
>> This is just an explicit way of writing the default, and
>> we do not expect that users will write it often unless
>> they're working with non-copyable types.
>>
>> - A function argument can be explicitly declared `shared`.
>>
>> ```
>> func ==(left: shared String, right: shared String) -> Bool {
>> ...
>> }
>> ```
>>
>> This cannot be combined with `owned` or `inout`.
>>
>> If the function argument is a storage reference expression,
>> that storage is accessed as a read for the duration of the
>> call. Otherwise, the argument expression is evaluated as
>> an r-value and that temporary value is shared for the call.
>> It's important to allow temporary values to be shared for
>> function arguments because many function parameters will be
>> marked as `shared` simply because the functions don't
>> actually from owning that parameter, not because it's in
>> any way semantically important that they be passed a
>> reference to an existing variable. For example, we expect
>> to change things like comparison operators to take their
>> parameters `shared`, because it needs to be possible to
>> compare non-copyable values without claiming them, but
>> this should not prevent programmers from comparing things
>> to literal values.
>>
>> Like `inout`, this is part of the function type. Unlike
>> `inout`, most function compatibility checks (such as override
>> and function conversion checking) should succeed with a
>> `shared` / `owned` mismatch. If a function with an `owned`
>> parameter is converted to (or overrides) a function with a
>> `shared` parameter, the argument type must actually be
>> copyable.
>>
>> - A method can be explicitly declared `consuming`.
>>
>> ```
>> consuming func moveElements(into collection: inout [Element]) {
>> ...
>> }
>> ```
>>
>> This causes `self` to be passed as an owned value and therefore
>> cannot be combined with `mutating` or `nonmutating`.
>>
>> `self` is still an immutable binding within the method.
>>
>> ### Function results
>>
>> As discussed at the start of this section, Swift's lexical
>> access model does not extend well to allowing ephemerals
>> to be returned from functions. Performing an access requires
>> executing storage-specific code at both the beginning and
>> the end of the access. After a function returns, it has no
>> further ability to execute code.
>>
>> We could, conceivably, return a callback along with the
>> ephemeral, with the expectation that the callback will be
>> executed when the caller is done with the ephemeral.
>> However, this alone would not be enough, because the callee
>> might be relying on guarantees from its caller. For example,
>> considering a `mutating` method on a `struct` which wants
>> to returns an `inout` reference to a stored property. The
>> correctness of this depends not only on the method being able
>> to clean up after the access to the property, but on the
>> continued validity of the variable to which `self` was bound.
>> What we really want is to maintain the current context in the
>> callee, along with all the active scopes in the caller, and
>> simply enter a new nested scope in the caller with the
>> ephemeral as a sort of argument. But this is a well-understood
>> situation in programming languages: it is just a kind of
>> co-routine. (Because of the scoping restrictions, it can
>> also be thought of as sugar for a callback function in
>> which `return`, `break`, etc. actually work as expected.)
>>
>> In fact, co-routines are useful for solving a number of
>> problems relating to ephemerals. We will explore this
>> idea in the next few sub-sections.
>>
>> ### `for` loops
>>
>> In the same sense that there are three interesting ways of
>> passing an argument, we can identify three interesting styles
>> of iterating over a sequence. Each of these can be expressed
>> with a `for` loop.
>>
>> #### Consuming iteration
>>
>> The first iteration style is what we're already familiar with
>> in Swift: a consuming iteration, where each step is presented
>> with an owned value. This is the only way we can iterate over
>> an arbitrary sequence where the values might be created on demand.
>> It is also important for working with collections of non-copyable
>> types because it allows the collection to be destructured and the
>> loop to take ownership of the elements. Because it takes ownership
>> of the values produced by a sequence, and because an arbitrary
>> sequence cannot be iterated multiple times, this is a
>> `consuming` operation on `Sequence`.
>>
>> This can be explicitly requested by declaring the iteration
>> variable `owned`:
>>
>> ```
>> for owned employee in company.employees {
>> newCompany.employees.append(employee)
>> }
>> ```
>>
>> It is also used implicitly when the requirements for a
>> non-mutating iteration are not met. (Among other things,
>> this is necessary for source compatibility.)
>>
>> The next two styles make sense only for collections.
>>
>> #### Non-mutating iteration
>>
>> A non-mutating iteration simply visits each of the elements
>> in the collection, leaving it intact and unmodified. We have
>> no reason to copy the elements; the iteration variable can
>> simply be bound to a shared value. This is a `nonmutating`
>> operaton on `Collection`.
>>
>> This can be explicitly requested by declaring the iteration
>> variable `shared`:
>>
>> ```
>> for shared employee in company.employees {
>> if !employee.respected { throw CatastrophicHRFailure() }
>> }
>> ```
>>
>> It is also used by default when the sequence type is known to
>> conform to `Collection`, since this is the optimal way of
>> iterating over a collection.
>>
>> ```
>> for employee in company.employees {
>> if !employee.respected { throw CatastrophicHRFailure() }
>> }
>> ```
>>
>> If the sequence operand is a storage reference expression,
>> the storage is accessed for the duration of the loop. Note
>> that this means that the Law of Exclusivity will implicitly
>> prevent the collection from being modified during the
>> iteration. Programs can explicitly request an iteration
>> over a copy of the value in that storage by using the `copy`
>> intrinsic function on the operand.
>>
>> #### Mutating iteration
>>
>> A mutating iteration visits each of the elements and
>> potentially changes it. The iteration variable is an
>> `inout` reference to the element. This is a `mutating`
>> operation on `MutableCollection`.
>>
>> This must be explicitly requested by declaring the
>> iteration variable `inout`:
>>
>> ```
>> for inout employee in company.employees {
>> employee.respected = true
>> }
>> ```
>>
>> The sequence operand must be a storage reference expression.
>> The storage will be accessed for the duraton of the loop,
>> which (as above) will prevent any other overlapping accesses
>> to the collection. (But this rule does not apply if the
>> collection type defines the operation as a non-mutating
>> operation, which e.g. a reference-semantics collection might.)
>>
>> #### Expressing mutating and non-mutating iteration
>>
>> Mutating and non-mutating iteration require the collection
>> to produce ephemeral values at each step. There are several
>> ways we could express this in the language, but one reasonable
>> approach would be to use co-routines. Since a co-routine does
>> not abandon its execution context when yielding a value to its
>> caller, it is reasonable to allow a co-routine to yield
>> multiple times, which corresponds very well to the basic
>> code pattern of a loop. This produces a kind of co-routine
>> often called a generator, which is used in several major
>> languages to conveniently implement iteration. In Swift,
>> to follow this pattern, we would need to allow the definition
>> of generator functions, e.g.:
>>
>> ```
>> mutating generator iterateMutable() -> inout Element {
>> var i = startIndex, e = endIndex
>> while i != e {
>> yield &self[i]
>> self.formIndex(after: &i)
>> }
>> }
>> ```
>>
>> On the client side, it is clear how this could be used to
>> implement `for` loops; what is less clear is the right way to
>> allow generators to be used directly by code. There are
>> interesting constraints on how the co-routine can be used
>> here because, as mentioned above, the entire co-routine
>> must logically execute within the scope of an access to the
>> base value. If, as is common for generators, the generator
>> function actually returns some sort of generator object,
>> the compiler must ensure that that object does not escape
>> that enclosing access. This is a significant source of
>> complexity.
>>
>> ### Generalized accessors
>>
>> Swift today provides very coarse tools for implementing
>> properties and subscripts: essentially, just `get` and `set`
>> methods. These tools are inadequate for tasks where
>> performance is critical because they don't allow direct
>> access to values without copies. The standard library
>> has access to a slightly broader set of tools which can
>> provide such direct access in limited cases, but they're
>> still quite weak, and we've been reluctant to expose them to
>> users for a variety of reasons.
>>
>> Ownership offers us an opportunity to revisit this problem
>> because `get` doesn't work for collections of non-copyable
>> types because it returns a value, which must therefore be
>> owned. The accessor really needs to be able to yield a
>> shared value instead of returning an owned one. Again,
>> one reasonable approach for allowing this is to use a
>> special kind of co-routine. Unlike a generator, this
>> co-routine would be required to yield exactly once.
>> And there is no need to design an explicit way for programmers
>> to invoke one because these would only be used in accessors.
>>
>> The idea is that, instead of defining `get` and `set`,
>> a storage declaration could define `read` and `modify`:
>>
>> ```
>> var x: String
>> var y: String
>> var first: String {
>> read {
>> if x < y { yield x }
>> else { yield y }
>> }
>> modify {
>> if x < y { yield &x }
>> else { yield &y }
>> }
>> }
>> ```
>>
>> A storage declaration must define either a `get` or a `read`
>> (or be a stored property), but not both.
>>
>> To be mutable, a storage declaration must also define either a
>> `set` or a `modify`. It may also choose to define *both*, in
>> which case `set` will be used for assignments and `modify` will
>> be used for modifications. This is useful for optimizing
>> certain complex computed properties because it allows modifications
>> to be done in-place without forcing simple assignments to first
>> read the old value; however, care must be taken to ensure that
>> the `modify` is consistent in behavior with the `get` and the
>> `set`.
>>
>> ### Intrinsic functions
>>
>> #### `move`
>>
>> The Swift optimizer will generally try to move values around
>> instead of copying them, but it can be useful to force its hand.
>> For this reason, we propose the `move` function. Conceptually,
>> `move` is simply a top-level function in the Swift standard
>> library:
>>
>> ```
>> func move<T>(_ value: T) -> T {
>> return value
>> }
>> ```
>>
>> However, it is blessed with some special semantics. It cannot
>> be used indirectly. The argument expression must be a reference
>> to some kind of local owning storage: either a `let`, a `var`,
>> or an `inout` binding. A call to `move` is evaluated by
>> semantically moving the current value out of the argument
>> variable and returning it as the type of the expression,
>> leaving the variable uninitialized for the purposes of the
>> definitive-initialization analysis. What happens with the
>> variable next depends on the kind of variable:
>>
>> - A `var` simply transitions back to being uninitialized.
>> Uses of it are illegal until it is assigned a new value and
>> thus reinitialized.
>>
>> - An `inout` binding is just like a `var`, except that it is
>> illegal for it to go out of scope uninitialized. That is,
>> if a program moves out of an `inout` binding, the program
>> must assign a new value to it before it can exit the scope
>> in any way (including by throwing an error). Note that the
>> safety of leaving an `inout` temporarily uninitialized
>> depends on the Law of Exclusivity.
>>
>> - A `let` cannot be reinitialized and so cannot be used
>> again at all.
>>
>> This should be a straightforward addition to the existing
>> definitive-initialization analysis, which proves that local
>> variables are initialized before use.
>>
>> #### `copy`
>>
>> `copy` is a top-level function in the Swift standard library:
>>
>> ```
>> func copy<T>(_ value: T) -> T {
>> return value
>> }
>> ```
>>
>> The argument must be a storage reference expression. The
>> semantics are exactly as given in the above code: the argument
>> value is returned. This is useful for several reasons:
>>
>> - It suppresses syntactic special-casing. For example, as
>> discussed above, if a `shared` argument is a storage
>> reference, that storage is normally accessed for the duration
>> of the call. The programmer can suppress this and force the
>> copy to complete before the call by calling `copy` on the
>> storage reference before
>>
>> - It is necessary for types that have suppressed implicit
>> copies. See the section below on non-copyable types.
>>
>> #### `endScope`
>>
>> `endScope` is a top-level function in the Swift standard library:
>>
>> ```
>> func endScope<T>(_ value: T) -> () {}
>> ```
>>
>> The argument must be a reference to a local `let`, `var`, or
>> standalone (non-parameter, non-loop) `inout` or `shared`
>> declaration. If it's a `let` or `var`, the variable is
>> immediately destroyed. If it's an `inout` or `shared`,
>> the access immediately ends.
>>
>> The definitive-initialization analysis must prove that
>> the declaration is not used after this call. It's an error
>> if the storage is a `var` that's been captured in an escaping
>> closure.
>>
>> This is useful for ending an access before control reaches the
>> end of a scope, as well as for micro-optimizing the destruction
>> of values.
>>
>> `endScope` provides a guarantee that the given variable has
>> been destroyed, or the given access has ended, by the time
>> of the execution of the call. It does not promise that these
>> things happen exactly at that point: the implementation is
>> still free to end them earlier.
>>
>> ### Lenses
>>
>> Currently, all storage reference expressions in Swift are *concrete*:
>> every component is statically resolvable to a storage declaration.
>> There is some recurring interest in the community in allowing programs
>> to abstract over storage, so that you might say:
>>
>> ```
>> let prop = Widget.weight
>> ```
>>
>> and then `prop` would be an abstract reference to the `weight`
>> property, and its type would be something like `(Widget) -> Double`.
>>
>> This feature is relevant to the ownership model because an ordinary
>> function result must be an owned value: not shared, and not mutable.
>> This means lenses could only be used to abstract *reads*, not
>> *writes*, and could only be created for copyable properties. It
>> also means code using lenses would involve more copies than the
>> equivalent code using concrete storage references.
>>
>> Suppose that, instead of being simple functions, lenses were their
>> own type of value. An application of a lens would be a storage
>> reference expression, but an *abstract* one which accessed
>> statically-unknown members. This would require the language
>> implementation to be able to perform that sort of access
>> dynamically. However, the problem of accessing an unknown
>> property is very much like the problem of accessing a known
>> property whose implementation is unknown; that is, the language
>> already has to do very similar things in order to implement
>> generics and resilience.
>>
>> Overall, such a feature would fit in very neatly with the
>> ownership model laid out here.
>>
>> ## Non-copyable types
>>
>> Non-copyable types are useful in a variety of expert situations.
>> For example, they can be used to efficiently express unique
>> ownership. They are also interesting for expressing values
>> that have some sort of independent identity, such as atomic
>> types. They can also be used as a formal mechanism for
>> encouraging code to work more efficiently with types that
>> might be expensive to copy, such as large struct types. The
>> unifying theme is that we do not want to allow the type to
>> be copied implicitly.
>>
>> The complexity of handling non-copyable types in Swift
>> comes from two main sources:
>>
>> - The language must provide tools for moving values around
>> and sharing them without forcing copies. We've already
>> proposed these tools in this document because they're
>> equally important for optimizing the use of copyable types.
>>
>> - The generics system has to be able to express generics over
>> non-copyable types without massively breaking source
>> compatibility and forcing non-copyable types on everybody.
>>
>> Otherwise, the feature itself is pretty small. The compiler
>> implicitly emits moves instead of copies, just like we discussed
>> above for the `move` intrinsic, and then diagnoses anything
>> that that didn't work for.
>>
>> ### `moveonly` contexts
>>
>> The generics problem is real, though. The most obvious way
>> to model copyability in Swift is to have a `Copyable`
>> protocol which types can conform to. An unconstrained type
>> parameter `T` would then not be assumed to be copyable.
>> Unfortunately, this would be a disaster for both source
>> compatibility and usability, because almost all the existing
>> generic code written in Swift assumes copyability, and
>> we really don't want programmers to have to worry about
>> non-copyable types in their first introduction to generic
>> code.
>>
>> Furthermore, we don't want types to have to explicitly
>> declare conformance to `Copyable`. That should be the
>> default.
>>
>> The logical solution is to maintain the default assumption
>> that all types are copyable, and then allow select contexts
>> to turn that assumption off. We will cause these contexts
>> `moveonly` contexts. All contexts lexically nested within
>> a `moveonly` context are also implicitly `moveonly`.
>>
>> A type can be a `moveonly` context:
>>
>> ```
>> moveonly struct Array<Element> {
>> // Element and Array<Element> are not assumed to be copyable here
>> }
>> ```
>>
>> This suppresses the `Copyable` assumption for the type
>> declared, its generic arguments (if any), and their
>> hierarchies of associated types.
>>
>> An extension can be a `moveonly` context:
>>
>> ```
>> moveonly extension Array {
>> // Element and Array<Element> are not assumed to be copyable here
>> }
>> ```
>>
>> A type can declare conditional copyability using a conditional
>> conformance:
>>
>> ```
>> moveonly extension Array: Copyable where Element: Copyable {
>> ...
>> }
>> ```
>>
>> Conformance to `Copyable`, conditional or not, is an
>> inherent property of a type and must be declared in the
>> same module that defines the type. (Or possibly even the
>> same file.)
>>
>> A non-`moveonly` extension of a type reintroduces the
>> copyability assumption for the type and its generic
>> arguments. This is necessary in order to allow standard
>> library types to support non-copyable elements without
>> breaking compatibility with existing extensions. If the
>> type doesn't declare any conformance to `Copyable`, giving
>> it a non-`moveonly` extension is an error.
>>
>> A function can be a `moveonly` context:
>>
>> ```
>> extension Array {
>> moveonly func report<U>(_ u: U)
>> }
>> ```
>>
>> This suppresses the copyability assumption for any new
>> generic arguments and their hierarchies of associated types.
>>
>> A lot of the details of `moveonly` contexts are still up
>> in the air. It is likely that we will need substantial
>> implementation experience before we can really settle on
>> the right design here.
>>
>> One possibility we're considering is that `moveonly`
>> contexts will also suppress the implicit copyability
>> assumption for values of copyable types. This would
>> provide an important optimization tool for code that
>> needs to be very careful about copies.
>>
>> ### `deinit` for non-copyable types
>>
>> A value type declared `moveonly` which does not conform
>> to `Copyable` (even conditionally) may define a `deinit`
>> method. `deinit` must be defined in the primary type
>> definition, not an extension.
>>
>> `deinit` will be called to destroy the value when it is
>> no longer required. This permits non-copyable types to be
>> used to express the unique ownership of resources. For
>> example, here is a simple file-handle type that ensures
>> that the handle is closed when the value is destroyed:
>>
>> ```
>> moveonly struct File {
>> var descriptor: Int32
>>
>> init(filename: String) throws {
>> descriptor = Darwin.open(filename, O_RDONLY)
>>
>> // Abnormally exiting 'init' at any point prevents deinit
>> // from being called.
>> if descriptor == -1 { throw ... }
>> }
>>
>> deinit {
>> _ = Darwin.close(descriptor)
>> }
>>
>> consuming func close() throws {
>> if Darwin.fsync(descriptor) != 0 { throw ... }
>>
>> // This is a consuming function, so it has ownership of self.
>> // It doesn't consume self in any other way, so it will
>> // destroy it when it exits by calling deinit. deinit
>> // will then handle actually closing the descriptor.
>> }
>> }
>> ```
>>
>> Swift is permitted to destroy a value (and thus call `deinit`)
>> at any time between its last use and its formal point of
>> destruction. The exact definition of "use" for the purposes
>> of this definition is not yet fully decided.
>>
>> If the value type is a `struct`, `self` can only be used
>> in `deinit` in order to refer to the stored properties
>> of the type. The stored properties of `self` are treated
>> like local `let` constants for the purposes of the
>> definitive-initialization analysis; that is, they are owned
>> by the `deinit` and can be moved out of.
>>
>> If the value type is an `enum`, `self` can only be used
>> in `deinit` as the operand of a `switch`. Within this
>> `switch`, any associated values are used to initialize
>> the corresponding bindings, which take ownership of those
>> values. Such a `switch` leaves `self` uninitialized.
>>
>> ### Explicitly-copyable types
>>
>> Another idea in the area of non-copyable types that we're
>> exploring is the ability to declare that a type cannot
>> be implicitly copied. For example, a very large struct
>> can formally be copied, but it might be an outsized
>> impact on performance if it is copied unnecessarily.
>> Such a type should conform to `Copyable`, and it should
>> be possible to request a copy with the `copy` function,
>> but the compiler should diagnose any implicit copies
>> the same way that it would diagnose copies of a
>> non-copyable type.
>>
>> ## Implementation priorities
>>
>> This document has laid out a large amount of work.
>> We can summarize it as follows:
>>
>> - Enforcing the Law of Exclusivity:
>>
>> - Static enforcement
>> - Dynamic enforcement
>> - Optimization of dynamic enforcement
>>
>> - New annotations and declarations:
>>
>> - `shared` parameters
>> - `consuming` methods
>> - Local `shared` and `inout` declarations
>>
>> - New intrinsics affecting DI:
>>
>> - The `move` function and its DI implications
>> - The `endScope` function and its DI implications
>>
>> - Co-routine features:
>>
>> - Generalized accessors
>> - Generators
>>
>> - Non-copyable types
>>
>> - Further design work
>> - DI enforcement
>> - `moveonly` contexts
>>
>> The single most important goal for the upcoming releases is
>> ABI stability. The prioritization and analysis of these
>> features must center around their impact on the ABI. With
>> that in mind, here are the primary ABI considerations:
>>
>> The Law of Exclusivity affects the ABI because it
>> changes the guarantees made for parameters. We must adopt
>> this rule before locking down on the ABI, or else we will
>> get stuck making conservative assumptions forever.
>> However, the details of how it is enforced do not affect
>> the ABI unless we choose to offload some of the work to the
>> runtime, which is not necessary and which can be changed
>> in future releases. (As a technical note, the Law
>> of Exclusivity is likely to have a major impact on the
>> optimizer; but this is an ordinary project-scheduling
>> consideration, not an ABI-affecting one.)
>>
>> The standard library is likely to enthusiastically adopt
>> ownership annotations on parameters. Those annotations will
>> affect the ABI of those library routines. Library
>> developers will need time in order to do this adoption,
>> but more importantly, they will need some way to validate
>> that their annotations are useful. Unfortunately, the best
>> way to do that validation is to implement non-copyable types,
>> which are otherwise very low on the priority list.
>>
>> The generalized accessors work includes changing the standard
>> set of "most general" accessors for properties and subscripts
>> from `get`/`set`/`materializeForSet` to (essentially)
>> `read`/`set`/`modify`. This affects the basic ABI of
>> all polymorphic property and subscript accesses, so it
>> needs to happen. However, this ABI change can be done
>> without actually taking the step of allowing co-routine-style
>> accessors to be defined in Swift. The important step is
>> just ensuring that the ABI we've settled on is good
>> enough for co-routines in the future.
>>
>> The generators work may involve changing the core collections
>> protocols. That will certainly affect the ABI. In contrast
>> with the generalized accessors, we will absolutely need
>> to implement generators in order to carry this out.
>>
>> Non-copyable types and algorithms only affect the ABI
>> inasmuch as they are adopted in the standard library.
>> If the library is going to extensively adopt them for
>> standard collections, that needs to happen before we
>> stabilize the ABI.
>>
>> The new local declarations and intrinsics do not affect the ABI.
>> (As is often the case, the work with the fewest implications
>> is also some of the easiest.)
>>
>> Adopting ownership and non-copyable types in the standard
>> library is likely to be a lot of work, but will be important
>> for the usability of non-copyable types. It would be very
>> limiting if it was not possible to create an `Array`
>> of non-copyable types.
>>
>> (end)
>>
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution